new和delete的实现
new
Class* pc = new Class ( 2 , 1 ) ;
等价于
void * mem = operator new ( sizeof ( Class) ) ;
pc = static_cast < Class* > ( mem) ;
pc- > Class:: Class ( 2 , 1 ) ;
delete
string* s = new string ( "hello" ) ;
delete s;
等价于
string:: ~ string ( s) ;
operator delete ( s) ;
new一个对象到底获得了多大内存 以下讨论均为在编译器VC内的情况,可能不同编译器有些许区别;
Complex* p1 = new Complex ( 2 , 1 ) ;
String* p2 = new String ( "lixu" ) ;
类Complex只有一个指针数据成员,占4bite,即图中绿色部分是我们本意想要获得的内存; 上下各有一块红色的4字节的16进制cookie用于告知操作系统,cookie中间夹着的内存(包括红色内存块在内)是否被分配出去;最后两位16进制代表分配的总内存大小,由于一定是16的倍数,因此最后一位必定为0.所以可以用最后一位为1来表示内存已经分配出去,则操作系统不能再使用这块内存;若delete之后,该位变成0,则操作系统知道这块内存现在可以被重新使用或被覆盖等等;如第一个为41,代表内存大小为64,且这块内存被分配出去了; 在调试模式下,(32位操作系统下)还会有灰色的:上面8块4bite内存,下面有一块4bite内存,即多出32+4; 若以上三者总内存不是16的倍数,则在绿色部分下面用蓝色部分补齐(一块4bite);
Complex* p1 = new Comples[ 3 ] ;
delete [ ] p1; / new [ ] ,则delete [ ] ;
String* p2 = new String[ 3 ] ;
delete [ ] p2;
相比上图中的new单个变量,new数组的话,在我们本意需要分配的那块内存上面,还需多分配4bite的int值,用于存该数组的元素个数;如下面中第一个白色的3代表分配了3个Complex;操作系统就知道下面的6块double是代表3个元素;则在delete[ ] p1时,调用三次析构函数(delete的底层实现是析构函数+free),每次释放一个元素; 上下的51h,31h等,意思和上图一样,代表分配的内存大小,1代表内存被分配出去了;末尾的h代表hex16进制; 对于下图的String来说,因为它的数据成员为一个指针,因此如果不加[ ],直接delete p1的话,编译器只知道调用一次析构函数,则只把数组的第一个成员指向的字符串释放了(即数组中第一个指针指向的内存释放掉了,并不知道数组还有别的指针);接下来的operator delete(即底层的free)操作会根据上下的cookie中保存的整个内存块的大小,释放掉包括cookie在内的整块内存(即也成功释放掉了new获得的3个指针成员本身),但除了第一个指针以外,其余指针指向的内存都没有被释放!造成内存泄漏;即即使new[ ]搭配delete,由于内存中记录了元素个数,所以这些元素本身都会被释放掉,只是当元素本身存储的是指针(指向动态分配的内存的话),这些动态分配的内存只会被释放一块,后面的内存泄露; 但对于Complex来说,因为它的数据成员为两个double而不是指针,所以即使不加[ ],直接delete p2的话,最终的结果也是没问题的;但好的习惯还是无论数组成员含不含指针,new[ ] 一定要搭配delete[ ]防止泄露;