目录
- 一、对new [] delete [] 的理解
- 1、delete的[]遗漏会带来什么影响
- 二、以示例探讨
- 三、cookie的理解
一、对new [] delete [] 的理解
new的对象是个array类型的。
Complex* pca = new Complex[3];
//唤起三次ctor
//无法借由参数给予初值
...
delete[] pca; //唤起3次dtor
如下图,new出来的是一个array,大小为3.
new的时候要调用3次ctor,delete的时候需要调用3次dtor。分配一个array的时候,会顺带分配一个cookie,用来记录信息,最主要的就是array的长度了。
1、delete的[]遗漏会带来什么影响
如果delete后面不加[],编译器会以为只需要delete所指的对象,所以只会调用一次dtor,然而cookie记录中的array长度并没有改变,此时就会少还一些内存给操作系统,从而导致内存泄漏。
str1、str2、str3会被完整回收,但是在回收之前需要调用析构函数。由于string在构造上会带有一个指针,指针指向真正的字符串的内存空间。调用三次dtor,会被很干净地清掉。而少加了[],会就只调用一次dtor,导致三块只释放掉了一块。
注意泄露的内存不是str1 2 3,而是指向的真正的字符串的内存空间。
**如果这里的array里面的元素类型是复数Complex,就不会造成内存泄漏,因为复数里面不包含指针。所以调用三次和调用一次也就无所谓了。**不过为了统一,还是要加上[]。
二、以示例探讨
示例代码:
A有一个默认构造函数,因为我们在new一个数组的时候不能一一地给定值,我们new是时候会调用三次构造函数。
构造函数和析构函数会在屏幕上输出占用内存位置
class A
{
public:int id;A() : id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; }A(int i) : id(i) { cout << "ctor. this=" << this << " id=" << id << endl; }~A() { cout << "dtor. this=" << this << " id=" << id << endl; }
};A* buf = new A[size]; //default ctor 3 次. [0]先於[1]先於[2])//A必須有 default ctor, 否則 [Error] no matching function for call to 'jj02::A::A()'A* tmp = buf; cout << "buf=" << buf << " tmp=" << tmp << endl; for(int i = 0; i < size; ++i)new (tmp++) A(i); //3次 ctor cout << "buf=" << buf << " tmp=" << tmp << endl;delete [] buf; //dtor three times (次序逆反, [2]先於[1]先於[0])
执行结果:
1、默认构造函数,默认id= 0 ;
2、this指针会自动移动,间距是一个对象的大小(int 4个字节)
3、移动指针,设初值,调用有参构造函数
4、需要注意这样的语法new(指针,指向已经分配的内存,我们在指针所指的地方进行设置初值) A(i)
,这属于placement new 的用法,之后的笔记会详细讲到。
5、循环过后id被修改,地址没有被修改
6、最后delete[],观察可知,调用了3次析构函数,析构的次序与构造的次序相反。(不同的编译环境析构次序可能不同)
三、cookie的理解
在做内存管理的时候,会有一个很大的诉求,就是不要这个cookie,所以cookie的存在以及大小是我们需要理解度的。
下面是VC6中,观察malloc给我们的内存布局:而我们获得的值指向分配的10个int数据的起始地址*pi.可以看到,除了我们认定的需要的10个int外,malloc还会分配32bytes和4bytes(橙色部分)。另外还有上cookie和下cookie,负责记录整块的大小。
另外还有一个pad区域,这是由于在VC6下,malloc分配的内存必须是16bytes的倍数,如果不是,则需要填充额外内存使之为16bytes的倍数。
注意cookie记录的分配的内存大小为60h,不过最后一个bit要被用做on or off 的状态的切换,所以为61h。(存疑,不是很理解这句话,之后再补上理解。)上下cookie的数值一样。
这里的delete加不加[]是没有影响的。
如果这里我们不是存放的int类型的数据,而是放的是一个对象,并且它的析构函数是有意义的,此时编译器创造array的方式会有所不同。
注意每个demo对象中存放的是三个int,我们new了3个demo,分配的内存与之前相比多了一个3,即3个demo。
delete不加[],编译器将p当做普通指针,指向一块对象,然后以一块对象的方式去解释布局,但是此刻的布局与之前不同,多了一个3,所以解释会发生错误。
使用array new和array delete时,内存块分配是不一样的。array元素个数被写到内存块里去了。
60h内存计算方式:
60h = 32(debugger header) + 4(3:元素个数,int类型,4个bytes) + 3 x 12 (3个 demo object) + 4 (no man land) +12(pad) + 4 x 2(上喜下两个cookie)= 96个byte = 60h个byte(pad是会变动的)