现在的位置: 首页 > 综合 > 正文

new/delete与new[]/delete[]必须配对使用

2017年12月06日 ⁄ 综合 ⁄ 共 2278字 ⁄ 字号 评论关闭
  1. void* operator new (size_t);       // allocate an object  
  2. void* operator new [] (size_t);    // allocate an array  
  3. void operator delete (void*);      // free an oject  
  4. void operator delete [] (void*);   // free an array 

熟悉C语言的朋友看到这里可能会很奇怪:在C语言中,无论申请的是单个对象,还是一个数组,管理内存所用的都是malloc/free,但是为什么到了C++里会出现两个呢?何况建议21中已经说明,new/delete在功能上比前者更加强劲。

先分析以下代码片段存在的问题:

  1. class Test  
  2. {  
  3. public:  
  4.     Test() { cout << "ctor" << endl; }  
  5.     ~Test() { cout << "dtor" << endl; }  
  6.     Hello(){ cout << "Hello C++" << endl; }  
  7. };  
  8. int main()  
  9. {  
  10.     cout <<"Test 1:"<< endl;  
  11.       Test* p1 = new Test[3];  
  12.       delete p1;  
  13.  
  14.       cout <<"Test 2:"<< endl;  
  15.       Test* p2 = new Test;  
  16.       delete[] p2;  
  17.  
  18.       return 0;  

上述代码看起来井然有序:我们采纳了建议21,用new完成了内存申请,并且使用了与之对应的delete来释放内存。可是,执行结果却显示上述代码存在问题。在Test 1中,构造函数调用了3次,构造了3个Test类型对象,而在删除时,却只析构了一个对象p1[0]。在Test 2中,构造函数调用了一次,但是析构函数却被调用了多次,将本不属于该对象的空间当成该类型对象进行了清理。也许各大厂商意识到了这个问题,于是让编译器能够检测出这样的错误。所以在VC++2010中,如果出现上述情形,编译器就会给出“debug
assertion failed”或“堆被损坏”的错误信息。

C++告诉我们在回收用new分配的单个对象的内存空间时用delete,在回收用new[]分配的一组对象的内存空间时用delete[]。下面我们就分析一下它们的实现原理。

无论new还是new[],C++必须知道返回指针所指向的内存块的大小,否则它就不可能正确地释放掉这块内存,这一点很像C语言中的malloc。但是在用new[]为一个数组申请内存时,编译器还会悄悄地在内存中保存一个整数,用来表示数组中元素的个数。因为在delete一块内存时,我们不仅要知道指针指向多大的内存,更重要的是要知道指针指向的数组中对象的个数。因为只有知道了对象数量才能一一调用它们的析构函数,完成对数组中所有对象的清理。如果使用的是delete,则编译器只会将指针所指的对象当作单个对象来处理。所以对于数组,需要使用delete[]来处理;符号[]会告诉编译器在delete这块内存时,先去获取保存的那个元素数量值,然后再进行一一清理。如果你对汇编有所了解,那么你可以通过反汇编代码对此一探究竟。

也许你会认为C++这么设计绝对是多此一举,因为单个对象只是对象数组的一个特例,无论是一个对象,还是对象数组,我们都对元素个数进行记录,这样也就不再需要两个版本的new和delete了。但是C++之父之所以没有选择这么做,也许是为了坚持他认定的C++设计风格和宗旨:决不多费一点力。殊不知,这么做的直接后果就是需要程序员付出更多的细心与努力。

需要注意的是,由于内置数据类型没有构造、析构函数,所以在针对内置数据类型时,释放内存使用delete或delete[]的效果是一样的。例如:

  1. int *pArray = new int[10];  
  2. ... // processing code  
  3. delete pArray; //等同于delete[] pArray; 

虽然针对内置类型,delete和delete[]都能正确地释放所申请的内存空间,但是如果申请的是一个数组,建议还是使用delete[]形式。

所以,使用new和delete的一个简单有效的原则就是:如果在调用new时使用了[],则你在调用delete时也使用[],如果在调用new的时候没有用[],那么也不应该在调用时使用[]。new和delete、new[]和delete[]必须对应着使用。

对于那些喜欢typedef的人,还有一点需要提醒。因为在这种情况下很容易出现new[]和delete的混用。如下面的代码片段所示:

  1. typedef int scorers[LESSONS_NUM];  
  2. int *pScorer = new scorers; 

这该使用哪一种形式的delete呢?如下所示。

  1. delete pScorer; // Wrong!!!  
  2. delete[] pScorer; // Right 

为了避免出现这样的错误,建议不要对数组类型做typedef,或者采用STL中的vector代替数组。

请记住:

new和delete、new[]和delete[]必须对应使用,否则会出现未定义行为,导致程序崩溃。

 

 

原文:http://book.51cto.com/art/201202/317620.htm

抱歉!评论已关闭.