1、不仅是malloc和freec++
new和delete都是C++中最基本的词汇,天天的代码都反复的与他们打交道,对他们最基本的认识就是分配和释放内存,最开始我认为他们就是C++版的malloc和free,可是其实内有玄机,除了分配内存,还能够用他们实现什么?函数
2、new和delete的完整过程spa
2.1 new 的完整过程能够分为三步,拿A* a=new A(1)这句为例:对象
operator new(size_t size)的默认实现是:接口
void *ptr = (void *)malloc(size);
return ptr; 内存
operator new( size_t count, void * object)的默认实现是 it
return object;内存管理
前两个步骤实际上是operator new的不一样overwrite,可能开始不太容易理解第二步的意义,第二步好像什么都没作,为何还要存在这种形式的operator new呢?实际上是由于这些operator new均可以被程序访问,例如咱们能够直接写出这样的代码完成new同样的操做:class
void * raw=operator new(sizeof(A));object
A* a=new(raw)A(1)
(固然调用operator new要include “new.h”,由于operator new不是c++原生的操做,定义在一个头文件中)
其中第一步是分配内存,第二步中的new(raw)部分就要调用operator new( size_t count, void * object)了,new(buffer)class(param)这种形式的new也被称为placement new,它的意思是在一个已经存在的内存上构造某个对象,operator new( size_t count, void * object)这个看似无心义的接口实际上是为了这种写法而作的。
2.2 delete ,delete 的完整过程也可分为两步,拿 delete* a举例:
因此咱们能够用下面代码替换delete a:
a.~A();
operator delete(raw);
2.3 上面是new和delete的完整过程,响应的还有new[]和delete[]的相似实现,这是分配与释放一个相连的多个实例:
void* operator new[](size_t size){
void *ptr = (void *)malloc(size);
return ptr;
}
void operator delete[](void* p){
free(p);
return;
}
下面两个是为了placement new[]和delete[]
void* operator new[](size_t size,void * object){
return object;
}
void operator delete[](void* p,void *p){
return;
}
因此一段传统的代码 A* a=new A[10] 和 delete[] a也能够经过这些operator new[]的调用来实现成以下:
void* raw=operator new(sizeof(A)*10+sizeof(int));
A* a=new(raw)A[10];
for(size_t i=0;i<10;i++){
fl2[i].~leon();
}
operator delete[](raw);
这里在分配内存的地方有个很特殊的地方,我想建立10个A,理应分配sizeof(A)*10大小的内存,可是对于operator new[](size_t size)的调用,必须多给他分配一个sizeof(int),写成上面的void* raw=operator new(sizeof(A)*10+sizeof(int));这是为何呢?
缘由在于这里是分配连续的N个内存,可是你必须让c++多用一个int来存储这个N,这个int多是放在这片内存的首位。
若是很少分配这个空间会怎样?那么operator delete[]时会出错,由于operator delete[]会首先读取一个int知道有多少个A要释放,你没有分配这个int,它可能读到一个不合理的值N,而后去释放,发现释放不了出现内存访问错误,或者N极大一直去释放,表现上程序永远结束不了。
3、重载你的new和delete
了解了上面的new和delete的过程能够怎样?既然C++已经为咱们封装好了new和delete,为何还要把他们赤裸裸的剥开。答案是:为了重载,为了定义咱们本身的new和delete。
首先一个问题,你能够重载new和delete吗?答案是不能,刚学C++重载时,C++的标准就告诉咱们new和delete操做是不能被重载的,可是有的时候咱们确实须要定制一些咱们本身的new和delete,(最简单的情形是我想统计我一共new了多少内存又delete了多少)。彷佛没有解决方法了,可是看到了new和delete的内部,咱们就有办法了,由于咱们能够重载组成new和delete的各个关键步骤的operator new和operator delete!这是一个使人兴奋的消息。
为了重载咱们要了解operator new 和delete各有几种形式,查查msdn,吓了一跳,包括[]形式一共有3*2*2=12种定义好的重载接口:
operator new:
A形式 这是new的第一步,分配内存
void *__cdecl operator new(
size_t count
);
B形式 这是for placement new
void *__cdecl operator new( size_t count, void * object ) throw();
C形式 这是不抛出异常的形式
void *__cdecl operator new(
size_t count,
const std::nothrow_t&
) throw();
operator delete:
void operator delete( void* _Ptr ) throw( ); void operator delete( void *, void * ) throw( ); void operator delete( void* _Ptr, const std::nothrow_t& ) throw( );
operator new[]:
void *operator new[]( std::size_t _Count ) throw(std::bad_alloc); void *operator new[]( std::size_t _Count, const std::nothrow_t& ) throw( ); void *operator new[]( std::size_t _Count, void* _Ptr ) throw( );
operator delete[]:
void operator delete[]( void* _Ptr ) throw( ); void operator delete[]( void *, void * ) throw( ); void operator delete[]( void* _Ptr, const std::nothrow_t& ) throw( );
C++的运算符重载分为全局重载和局部的类内的重载。
3.1 首先的全局重载,全局重载意味着代码内全部的new和delete的行为都被改变(包括你引用的库),要十分当心,甚至不少人说不要使用全局重载new和delete。
下面是我全局重载operator new和new[]的第一种A形式(C形式雷同),注意在以上这些操做中,在VS中A和C种方法均可以被用户全局重载,可是B种形式(也就是placement 形式)不容许被重载(它实现成了inline函数),不知道其余IDE是怎样的 规定,可是VS的规定是有道理的,由于咱们几乎想不出有什么理由要placement new,它本就是一种为形式而生的形式。。。
//overload global operator new
void* operator new(size_t size){
cout<<"new "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
//overload global operator delete
void operator delete(void* p){
cout<<"delete "<<_msize(p)<<endl;
free(p);
return;
}
//overload global operator new[]
void* operator new[](size_t size){
cout<<"new[] "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
//overload global operator new[]
void operator delete[](void* p){
cout<<"delete[] "<<_msize(p)<<endl;
free(p);
return;
}
在个人重载中对于每次施放和分配都额外打印当前涉及内存的大小
3.2 类内重载,这种重载更加经常使用一些,这里要注意,对于类内重载,若是要调用placement new,那么B形式必须被重载,不然你的代码编不过,这与全局重载彻底不一样,如下是我对类内实现的A和B形式的重载:
class leon{
public:
leon(){
m_a=0;
}
leon(int a){
m_a=a;
}
~leon(){
}
//overload operator new
void* operator new(size_t size){
cout<<"new "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
void* operator new(size_t size,void* buf){
cout<<"new "<<size<<endl;
return buf;
}
//overload operator delete
void operator delete(void* p){
cout<<"delete "<<_msize(p)<<endl;
free(p);
return;
}
//overload global operator new[]
void* operator new[](size_t size){
cout<<"new[] "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
void* operator new[](size_t size,void* buf){
cout<<"new[] "<<size<<endl;
return buf;
}
//overload operator new[]
void operator delete[](void* p){
cout<<"delete[] "<<_msize(p)<<endl;
free(p);
return;
}
3.3 更多形式的重载:
其实除了c++定义好的A、B、C三种operator new()接口,咱们还能够自定义新的重载形似,如定义一个void* operator new(size_t size,bool b),那么我就能够调用A* a=operator(sizeof(A),true)来执行个人一些特殊需求
4、operator new和delete及其重载的应用:
对new和delete重载能够在不少地方应用到:
最多见的就是一些内存管理和统计功能:你想知道你分配和释放了多少内存
内存泄露的监测:经过统计能够知道是否有内存泄露,甚至定位到于某处有泄露
另外经过使用placement new还能够在某些场合加快对象的建立,好比你能够事先分配好一片内存,而后利用placement new在上面直接建立,这样以后的每次建立不会再有反复的内存分配和释放操做
利用上面的特性能够实现某种内存池来加速内存访问
以上只是抛砖引玉,之后能够再实践中多利用C++的这个特性