问题实例
1 | string * stringarray = new string[100]; |
在上述代码中我们成对地使用了new
与delete
,似乎避开了资源泄露,但其实并非如此。
上文动态分配了100个string对象,却只删除了第一个,其它string对象的析构函数根本没有被调用。
new与delete
调用new与delete时发生了什么?
当你使用new的时候,有两件事会发生:
- 内存通过
operator new
被分配出来(new操作符详见Effective C++ 50 52) - 针对此内存有一个或多个构造函数被调用
对应的,使用delete的时候也会发生两件事:
- 一个或多个析构函数被调用
- 通过
operator delete
释放内存
delete如何判定对象个数?
delete的最大问题在于:即将被删除的内存之内究竟存有多少对象?这决定了会调用多少次析构函数。换句话说,delete需要明确:被删除的指针究竟指向单一对象还是对象数组?
在编译器的具体实现中,单一对象的内存布局不同于数组的内存布局,数组的内存布局中记录了数组大小,这得以让delete函数知道需要调用多少次析构函数。它们的内存布局大概是这样:
delete的正确使用
我们通过人为指定的方式让delete明确其操作:使用delete或delete[];
前者认定当前指向的是单一对象,后者则认定当前指向数组:1
2
3
4string* pstr1 = new string;
string* pstr2 = new string[100];
delete pstr1;
delete[] pstr2;
误用delete形式的后果
如果我们对pstr1
使用delete[]
,结果未定义,可想而知它会误认为当前指向某一个对象数组,然后读取某块内存将其解释为数组大小,然后反复调用析构函数。
如果我们对pstr2
没有使用delete[]
,结果亦未有定义,但肯定调用的析构函数不足。事实上,即使针对没有析构函数的内置类型,这种写法也是有害的。
new、delete与typedef
本节的规则十分简单,无非是成对使用new与delete时必须保证形式一致,读者想必会认为稍加注意不难做到。但对于某些重度typedef爱好者而言,还是要多加留心为是:1
2
3
4typedef string AddressLines[4];
string* pal = new AddressLines;//pal指向的是数组对象
delete pal;//error!
delete[] pal;
我认为C++语言应当尽量减少对数组使用typedef定义,在降低了可读性的同时还容易引发错误。在能够使用STL容器的地方坚决不使用动态数组。