我们知道delete用于释放一个动态空间,而delete[ ]用于释放多个动态空间,但是如果我们混用的话会导致什么问题?我在博客上看了许多文章,但不尽人意,因此写下这篇文章让你知其然知其所以然(浅薄的解释,望指正)。


先看一个案例吧

程序为:

class A 
{
private:
    int _a;
public:
    A(int x = 0) 
    {
        _a = x;
    }
    ~A() 
    {
    }
};

int main()
{
    A* p = new A[10];
    delete p;//混用delete
}

运行结果为:

但是如果我们不显式地写析构函数:

class A 
{
private:
    int _a;
public:
    A(int x = 0) 
    {
        _a = x;
    }
   /* ~A() 
    {
    }*/
};

int main()
{
    A* p = new A[10];
    delete p;
}

结果是正常运行:


为什么会有上面的两种现象呢?

如果我问你在上面的例子中系统分配了多少空间给这10个A类对象,那你大概率会回答40字节。很好理解嘛,一个A是4个字节,10个就是40字节。

但是实际上,如果你给对象自己写了析构函数,那么总共分配了44个字节,如果没写析构函数那么才总共分配40个字节!

为什么写了析构函数就多了4个字节

  • 我们知道用delete[]释放这块空间后会自动调用析构函数,此时如果我们自己写了析构函数,delete[]就需要去调用它,那么谁告诉delete[]要调用几次析构函数,也就是我怎么知道这块空间里面有几个对象?实际上,多出来的这四个字节恰好是一个整形的大小,这个整形用以记录这块空间的对象个数,从而确定需要调用几次构造和析构函数。也就是说delete[]在释放一个对象的时候,先要获取对象个数,然后调用析构函数,而delete则不会获取对象个数,直接调用一次析构函数(因为delete默认就是为了处理单个对象)

不过,我们要知道,p指针指向的仍然是第一个对象,只是在构造和析构时会向前走获取一下对象个数,具体关系如下:

为什么不写析构函数就不会多这4个字节?

  • 如果此时我们没写析构函数,他就会调用编译器生成的析构函数,不用记录需要析构的次数,也就不用多开一个整形。
  • 对于new出来的内置类型的数组空间,也不会记录需要析构的次数,混用不会报错,因为内置类型析构函数默认什么都不做,所以也没必要调用。

好,现在可以解释上面的例子了:

  • delete默认从指针指向的位置释放空间,不去获取对象个数。而delete[ ]默认先前移获取对象个数,然后调用若干次析构函数,最后再释放内存。
  • 如果多开了空间但仍然用delete释放空间的话指针不会先前移访问开头的4字节,不从内存起始地址进行释放操作就会出现段错误,但不写析构函数也就不会多开四字节空间,从而避免了这个错误
  • 有趣的是,如果你使用delete[ ]释放单个对象,那么情况恰恰相反,只有写了析构函数才会报错,不论如何,记住上面的事实,就可以预测这种错误。

练习一下这个会报错吗?

不用调用析构函数也就不用计录对象个数,也就不多开空间,因此可以从指针指向位置释放空间,所以不会报错。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐