《Effective C++》Item16:成对使用new/delete和new[]/delete[]

以下代码有什么问题?

std::string *stringArray = new std::string[100];
//...
delete stringArray;

每件事情看起来都井然有序。使用了new,也搭配了对应的delete。但还是有一点完全错误:程序的行为是未定义的。在最好的情况下,stringArray所包含的100个string对象中的99个不太可能被正确地回收,因为它们的析构函数很可能没有被调用。

当使用了new的时候,有两件事情发生:

  • 调用operator new分配内存。
  • 对象的构造函数被调用。

当使用了delete的时候,也有两件事情发生:

  • 对象的析构函数被调用。
  • 调用operator delete释放内存。

delete的过程当中,最大的问题是:即将被回收的内存中究竟存有多少个对象?这个问题的回答决定了有多少个析构函数必须被调用起来。

对于我们而言,实际上这个问题可以更简单些:即将被回收的那个指针,所指的是一个单一的对象,还是一个对象的数组?这是一个不可或缺的问题,因为单一对象的内存布局一般而言不同于数组的内存布局。更明确地说,数组所用的内存通常还包括“数组大小”的记录,以便于delete知道需要调用多少次析构函数;而单一对象的内存则没有这笔记录。

可以简单地将两种内存排列方式理解如下(这是一种假设,用于帮助理解,但是很多编译器确实也是这么做的):

  • new的内存排布
    在这里插入图片描述
  • new[]的内存排布
    在这里插入图片描述

当我们对着一个指针使用delete的时候,唯一能够让delete知道内存中是否存在一个“数组大小记录”的方法就是:我们自己告诉它。也就是说,如果在delete操作中添加了[]delete就会认定这个指针指向了一个数组,否则就是单一的一个对象。

std::string *ptr1 = new std::string;
std::string *ptr2 = new std::string[100];
//...
delete ptr1; //删除单一的一个对象。
delete ptr2; //删除一个由对象构成的数组。

现在假设我们对ptr1使用delete[],会发生什么呢?delete可能会读取若干字节的内存并将它解释为“数组大小”,然后从下一个地址开始连续调用析构函数,浑然不知它所处理的那块内存不但不是个数组,也或许并未持有那种类型的对象。

如果对ptr2使用delete又会发生什么事呢?未有定义,但是可以预见析构函数至多会被调用一次;更有甚者,对内置类型如int来说,即便它没有析构函数,仍然是有害的。

现在,规则很清晰了:如果在new的时候使用了[],那么delete的时候也应该使用[];如果在new的时候没有使用[],那么delete的时候也不应该使用[]

其实,为了避免类似的错误,最好不要使用数组这种元素,因为STL当中包含有stringvector等等的模板类,我们对原生数组的需求几乎被降至了0。

【注意】
如果在new的时候使用了[],那么delete的时候也应该使用[];如果在new的时候没有使用[],那么delete的时候也不应该使用[]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值