参考:C++new和delete实现原理

一、new[]数组

  • new[]也分为两种情况:
  • 1、简单数据类型( 包括基本数据类型和不需要析构函数的类型)。
    new[] 调用的是operator new[],计算出数组总大小之后调用operator new。
    值得一提的是,可以通过()初始化数组为零值,实例:
    char* p = new char[32]();
    等同于:
    char *p = new char[32];
    memset(p, 32, 0);

总结:
针对简单类型,new[]计算好大小后调用operator new。

  • 2、复杂数据类型(需要由析构函数销毁对象)
    实例:

    class Object
    {
    public:
      Object()
      {
          _val = 1;
      }
    
      ~Object()
      {
          cout << "destroy object" << endl;
      }
    private:
      int _val;
    };
    void main()
    {
      Object* p = new Object[3];
    }

    new[]先调用operator new[]分配内存,然后在p的前四个字节写入数组大小,最后调用三次构造函数。
    实际分配的内存块如下:
    图片说明

这里为什么要写入数组大小呢?因为对象析构时不得不用这个值。
释放内存之前会调用每个对象的析构函数。但是编译器并不知道p实际所指对象的大小。如果没有储存数组大小,编译器如何知道该把p所指的内存分为几次来调用析构函数呢?
总结:
针对复杂类型,new[]会额外存储数组大小。

二、delete数组
delete[]也分为两种情况:
1,简单数据类型( 包括基本数据类型和不需要析构函数的类型)。
delete和delete[]效果一样
比如下面的代码:

int* pint = new int[32];
delete pint;

char* pch = new char[32];
delete pch;

运行后不会有什么问题,内存也能完成的被释放。看下汇编码就知道operator delete[]就是简单的调用operator delete。
总结:
针对简单类型,delete和delete[]等同。

2,复杂数据类型(需要由析构函数销毁对象)
释放内存之前会先调用每个对象的析构函数。
new[]分配的内存只能由delete[]释放。如果由delete释放会崩溃,为什么会崩溃呢?
假设指针p指向new[]分配的内存。因为要4字节存储数组大小,实际分配的内存地址为[p-4],系统记录的也是这个地址。delete[]实际释放的就是p-4指向的内存。而delete会直接释放p指向的内存,这个内存根本没有被系统记录,所以会崩溃。
总结:
针对复杂类型,new[]出来的内存只能由delete[]释放。