C++箴言:使用相同形式的new和delete

C++箴言:使用相同形式的new和delete,第1张

C++箴言:使用相同形式的new和delete,第2张

以下代码有什么问题?

STD::string * string array = new STD::string[100];
...
删除stringArray

一切似乎都很正常。New也与delete成对出现。但是,还是有完全不对的地方。程序的行为是未定义的。直到最后,stringArray指向的100个string对象中,有99个不太可能被完全销毁,因为它们的析构函数可能根本不会被调用。

当您使用一个新的表达式(也就是说,使用new动态创建一个对象)时,会发生两件事。首先,分配内存(通过一个名为operator new的函数——参见第49和51项)。其次,在这些内存上调用一个或多个构造函数。当你使用一个删除表达式(也就是使用delete)时,还会发生另外两件事:在这些内存上调用一个或多个析构函数,然后回收内存(通过一个叫做操作符delete的函数——见第51项)。delete有一个大问题:内存中有多少要删除的对象?这个问题的答案将决定需要调用多少个析构函数。

其实问题很简单:要删除的指针是指向单个对象还是对象数组?这是一个关键问题,因为单个对象的内存布局通常不同于数组。具体来说,数组的内存布局通常包含数组的大小,这使得delete更容易知道需要调用多少个析构函数。但是单个对象的内存缺少这些信息。你可以认为不同的内存布局如下图所示,n是数组的大小:


当然,这只是一个例子。编译器不一定要这样做,尽管很多编译器都这样做。

当你对一个指针使用delete时,知道是否有数组大小信息的方法就是你告诉它。如果在使用的delete中加上方括号,delete会假设指针指向一个数组。否则,假定它指向单个对象。

STD::string * string ptr 1 = new STD::string;
STD::string * string ptr 2 = new STD::string[100];
...
删除stringPtr1//删除一个对象
delete[]string ptr 2;//删除对象数组

如果对stringPtr1使用[]形式会发生什么?结果未定义,但不太可能是好事。假设上图所示的布局,delete会读入某个内存的内容,并把它当成一个数组的大小,然后开始调用这么多的析构函数,不仅完全忽略了它所作用的内存不是一个数组,还有可能忘记了它正在忙着析构的对象的类型。

如果不使用stringPtr2的[]形式会怎么样?它也是未定义的,只是你不会看到它会导致调用太多的析构函数。此外,像int这样的内置类型的结果是未定义的(有时是有害的),即使这类类型没有析构函数。

规则很简单。如果在新表达式中使用[],还必须在相应的删除表达式中使用[]。如果在新表达式中不使用[],就不要在匹配的删除表达式中使用[]。

当您编写一个包含指向动态分配内存的指针并提供多个构造函数的类时,这条规则尤其重要,因为这时您必须在所有构造函数中用相同形式的new小心地初始化指针成员。如果你不这样做,你怎么知道在你的析构函数中应该使用哪种形式的删除?

这个规则对于倾向于typedef的人来说也是值得注意的,因为这意味着typedef的作者必须在文档中记录:用new生成typedef类型的对象时,应该使用哪种形式的delete。例如,考虑以下typedef:

typedef STD::string address lines[4];//一个人的地址有4行,
//每行都是一个字符串

因为AddressLines是一个数组,所以这里使用new,

STD::string * pal = new address lines;//请注意,“新地址行”
//返回一个字符串*,就像
//“新字符串[4]”一样

必须以删除数组的形式匹配:

删除pal//未定义!
删除[]pal;//好吧

为了避免这种混淆,我们应该避免对数组类型使用typedef。这很简单,因为标准C++库(见第54项)包含了字符串和向量,那些模板将动态分配数组的需求降低到几乎为零。例如,在这里,AddressLines可以定义为string的向量,即类型为vector。

要记住的事情

如果在新表达式中使用[],则必须在相应的删除表达式中使用[]。如果在new表达式中不使用[],那么在对应的delete表达式中就不必使用[]。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » C++箴言:使用相同形式的new和delete

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情