互相联系的 new 和 delete 要使用同样的形式

来源:互联网 发布:淘宝开店做什么产品好 编辑:程序博客网 时间:2024/06/08 14:52
 
请观察下面的代码有什么不妥之处:
std::string *stringArray = new std::string[100];
...
delete stringArray;
一切似乎都按部就班, new 语句与 delete 相匹配。然而,这却是十分错误的。这段程序将出现无法预知的行为。最起码的是,由于该 stringArray所指向的 100 个 string对象中的99个没有被析构函数所析构,它们将很有可能得不到销毁。
 
当使用了一个new语句时(也可以说,使用new动态创建了一个对象),将会发生两件事情。第一,分配内存(通过一个名为operator new的函数)。第二,为这段内存调用一个或多个构造函数。当使用了一个delete语句时,将会发生另外两件事情:第一,为分配的内存调用一个或多个析构函数。第二,释放内存(通过operator delete函数实现)。delete的关键问题是:内存中存在多少需要删除的对象呢?答案取决于需要调用多少析构函数。
实际上,答案十分简单,那就是:指针是指向一个单独的对象,还是一组对象?这个问题很关键,因为为单个对象分配的内存与为一系列对象分配的内存在形式上有本质的不同。具体地说,为数组分配的内存通常要保存数组的大小,这就使得delete很容易知道需要调用多少次析构函数。为单个对象分配的内存则不保存这一信息。
 
当对一个指针使用delete时,如何让delete知道这一指针是否存在数组信息呢?这里只有一种方法,那就是亲自告诉它。如果在delete与指针名之间添加一对中括号,则delete便认为这一指针指向一个数组。否则将以单一对象处理。
std::string *stringPtr1 = new std::string;
std::string *stringPtr2 = new std::string[100];
...
delete stringPtr1; // 删除一个对象
delete [] stringPtr2;   // 删除一个对象数组
 
如果为stringPtr1使用“[]”时将会发生什么呢?我们说,这样做不会得到预期的效果。假设使用上面的内存分配形式,delete将会读入一些内存信息,并且将其理解为数组的长度,然后便开始调用这么多的析构函数,此时delete不仅忽视了它正在操作的内存上保存的并不是数组,同时它“辛辛苦苦”析构的东西很有可能都不是它所能操作的类型。
如果不为stringPtr2使用“[]”将会发生什么呢?我们也可以说,这样做同样的不到预期的效果。可以看到由于它没有调用足够的析构函数,于是将造成内存泄漏。同时,对于内建数据类型,诸如int等,尽管它们没有析构函数,但同样也会带来无法预期的结果(有时是有害的)。
 
这里的规则很简单:如果在一个new语句中使用了[],那么必须在相关的delete语句中也使用[]。反之亦然。
有时候我们会编写这样的类:它们包含用来动态分配内存的指针,并且提供多个构造函数。此时需要时刻注意遵守上面的规则。在所有的构造函数中,当编写初始化指针成员的语句时,必须使用new一致的格式。如果不这样做,那么怎么能知道析构函数中delete需要用什么样的格式呢?
如果倾向于使用typedef,那么这一规则同样值得注意,因为它意味着typedef的创建者必须清楚:当typedef的类型中使用了new来创建对象,那么相应的delete语句中必须要使用同样的格式。请看下边的示例:
typedef std::string AddressLines[4];
    // 每个人的地址有 4行,
    // 每行都是一个字符串
由于AddressLines是一个数组,如果这样使用了new
std::string *pal = new AddressLines;
    // 请注意“ new AddressLines
    // 返回一个 string* ,与“ new string[4]”完全一样
那么delete就必须使用数组的格式:
delete pal; // 将出现无法预知的行为!
delete [] pal;                        // 工作正常
 
为了避免此类混淆,请谨慎使用typedef来定义数组。这十分简单,因为标准C++库中包含了stringvector,使用这些模板可以摆脱动态分配数组的烦恼。比如说,在这里,AddressLines可以定义为一个字符串的向量,也就是vector<string>类型。
 
牢记在心
如果在一个new语句中使用了[],那么必须要在相关的delete语句中使用[]。如果在new语句中没有使用[],那么在相关的delete语句中一定不要出现[]
 
 
原创粉丝点击