关于new和delete,new[] 和delete[] - phymelinda的专栏 ...

来源:百度文库 编辑:神马文学网 时间:2024/04/23 23:10:51
通常状况下,编译器在new的时候会返回用户申请的内存空间大小,但是实际上,编译器会分配更大的空间,目的就是在delete的时候能够准确的释放这段空间。
这段空间在用户取得的指针之前以及用户空间末尾之后存放。
实际上:blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;其中,blockSize 是系统所分配的实际空间大小,_CrtMemBlockHeader是new的头部信息,其中包含用户申请的空间大小等其他一些信息。nNoMansLandSize是尾部的越界校验大小,一般是4个字节“FEFEFEFE”,如果用户越界写入这段空间,则校验的时候会assert。用户new的时候分为两种情况
A.new的是基础数据类型或者是没有自定义析构函数的结构
B.new的是有自定义析构函数的结构体或类这两者的区别是如果有用户自定义的析构函数,则delete的时候必须要调用析构函数那么编译器delete的如何知道要调用多少个对象的析构函数呢,答案就是new的时候,如果是情况B,则编译器会在new头部之后,用户获得的指针之前多分配4个字节的空间用来记录new的时候的数组大小,这样delete的时候就可以取到个数并正确的调用。
下面我们就来看看下面几个问题:示例一:
int *pInt = new int[10];
delete pInt;这种情况下,由于编译器不会再pInt之前分配4个字节,所以delete的时候会直接从new的头部信息里取得大小并释放,此时delete与delete[]等价。示例二:
Class A
{
public:
    ~A();
    int a;
}A *pA = new A[10];
delete pA;这种情况下,pA之前的四个字节记录了用户申请的A对象的个数,即10。但是在delete的时候,只调用了A[0]的析构函数,造成了后面9个对象的析构函数没有调用,可能造成类的内存泄漏。但是,问题并没有这么简单。系统在释放对象本身的内存的时候,认为pA只是单独的对象指针,则释放的时候编译器寻找new头部信息的时候会把这4个字节计算进去,显然,得到的头部信息是错的,当然也就造成了释放这段内存会出错了。示例三:
A *pA = new A;
delete[] pA;这种情况与情况二类似,delete[]会取pA前的4个字节当做用户申请对象的个数,并调用析构,显然此时就会出错。综合上述,在特定情况下,new[]分配的内存用delete不会出错,但是大多情况下会产生严重问题,所以必须将new和delete,new[]和delete[]配套使用。 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/phymelinda/archive/2010/03/12/5373209.aspx 

1. malloc/free 与 new/delete的不同之处:

  • malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。对于非内部数据类的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数, 对象消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。而对于new和delete,编译器会帮我们完成大部分操作。
  • 我们在使用malloc开辟内存时,必须自己指定内存量,也就是说,必须使用sizeof等得到对象大小的操作符,如
    int *p = (int *) malloc(sizeof(int) * length);
    malloc 返回值的类型是void *,所以在调用malloc 时要显式地进行类型转换,将void * 转换成所需要的指针类型。malloc 函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。
    而用new则无需用sizeof,也不用管变量类型,编译器会帮我们来搞定这个事情。
  • 重复的去free一段内存就会出错,而重复去delete一段对象则不会有事。

        总而言之,可以简单归纳成以下几点:

  • new自动计算需要分配的空间,而malloc需要手工计算字节数
  • new是类型安全的,而malloc不是,比如:
    int* p = new float[2];       // 编译时指出错误
    int* p = malloc(2*sizeof(float));       // 编译时无法指出错误
    new operator 由两步构成,分别是 operator new 和 construct
  • operator new对应于malloc,但operator new可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上。而malloc无能为力
  • new将调用constructor,而malloc不能;delete将调用destructor,而free不能。
  • malloc/free要库文件支持,new/delete则不要。

2. new/delete/new []/delete[] ’s action

  • 调用new所包含的动作:
    从系统堆中申请恰当的一块内存。
    若是对象,调用相应类的构造函数,并以刚申请的内存地址作为this参数。
  • 调用new[n]所包含的动作:
    从系统堆中申请可容纳n个对象外加一个整型的一块内存;
     将n记录在额外的哪个整型内存中。(其位置依赖于不同的实现,有的在申请的内存块开头,有的在末尾);
    调用n次构造函数初始化这块内存中的n个连续对象。
  • 调用delete所包含的动作:
    若是对象,调用相应类的析构函数(在delete参数所指的内存处);
    该该内存返回系统堆。
  • 调用delete[]所包含的动作:
    从new[]记录n的地方将n值找出;
    调用n次析构函数析构这快内存中的n个连续对象;
    将这一整快内存(包括记录n的整数)归还系统堆。

3. 什么时候需要自己亲自循环来delete数组中的元素?

      如果你的对象数组的每个元素是对象指针(也就是说,你其实是开辟了一个指针数组),那你就必须亲自循环来delete每一个数组元素。否则的话,你只须简单的用delete []a; 就OK了。

4. 我们在new一个数组时,会有数组大小size记录,对于数组,我们必须用delete[]来删除对象。我们能否学习free的做法,用delete就实现delete[]功能?也就是说,假设A  * a = new A[10],a是一个数组,那么能否用delete a;来代替delete []a;?

    一般来说,因为我们在new对象数组时,我们会记录它的大小信息,因此,想用delete a;来实现delete []a;也不无可能。只要在delete a;判断它是否有数组长度值,如果有就删除数组,如果没有,就单单删除对象就好。我们在用delete删除一个对象时,其实在堆中也会有该对象的大小,所以这样才能保证删除不会有误。但是,这样问题就来了,以前delete[]时编译器知道是数组,所以会去找数组长度,然后用delete去逐一删除对象(删除对象时也必须得到每个对象的大小),而现在如果用delete来代码delete[] ,delete只能得到该数组的长度,删除时就无法删除所有对象。(C++标准中把这种行为确认为“无定义”)