关于C++中new/delete和new[]/delete[]

参看链接
浅谈 C++ 中的 new/delete 和 new[]/delete[]

operator new 和 operator delete

这两个其实是 C++ 语言标准库的库函数,原型分别如下:

void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an object

void *operator new[](size_t);     //allocate an array
void *operator delete[](void *);    //free an array

new 和 delete 背后机制

class A
{
public:
    A(int v) : var(v)
    {
        fopen_s(&file, "test", "r");
    }
    ~A()
    {
        fclose(file);
    }

private:
    int var;
    FILE *file;
};
class A *pA = new A(10);

来创建一个类的对象,返回其指针pA。如下图所示 new背后完成的工作:

总结一下:

  • 首先需要调用上面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,你可以看看《深入 C++ 对象模型》一书,这里不做多解释。这样函数返回的是分配内存的起始地址,这里假设是0x007da290
    上面分配的内存是未初始化的,也是未类型化的,
  • 第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,这里是调用 A:A(10); 这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, `file 指向打开的文件。
  • 最后一步就是返回新分配并构造好的对象的指针,这里 pA 就指向 0x007da290 这块内存,pA的类型为类 A 对象的指针。
delete pA;

delete 所做的事情如下图所示:

delete就做了两件事情:

  • 调用 pA 指向对象的析构函数,对打开的文件进行关闭。
  • 通过上面提到的标准库函数operator delete 来释放该对象的内存,传入函数的参数为pA的值,也就是 0x007d290

如何申请和释放一个数组?

new [] 一个对象数组时,需要保存数组的维度,C++ 的做法是在分配数组空间时多分配了 4 个字节 的大小,专门保存数组的大小,在 delete []时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。

调用

class A *pAa = new A[3];

时需要做的事情如下:

delete []pAa;

这里要注意的两点是

  • 调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
  • 传入 operator delete[] 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。

为什么 new/delete 、new []/delete[] 要配对使用?

如果是带有自定义析构函数的类类型,用 new [] 来创建类对象数组,而用 delete来释放会发生什么?用上面的例子来说明:

class A *pAa = new class A[3];
delete pAa;

那么 delete pAa;做了两件事:

  • 调用一次 pAa指向的对象的析构函数;
  • 调用 operator delete(pAa);释放内存。

显然,这里只对数组的第一个类对象调用了析构函数,后面的两个对象均没调用析构函数,如果类对象中申请了大量的内存需要在析构函数中释放,而你却在销毁数组对象时少调用了析构函数,这会造成内存泄漏。

上面的问题你如果说没关系的话,那么第二点就是致命的了!直接释放pAa指向的内存空间,这个总是会造成严重的段错误,程序必然会奔溃!因为分配的空间的起始地址是 pAa 指向的地方减去 4 个字节的地方。你应该传入参数设为那个地址!

猜你喜欢

转载自blog.csdn.net/a731062834/article/details/82967372