2 qq906237201 qq906237201 于 2016.05.01 17:52 提问

不用虚析构函数也不会造成内存泄漏的原因是什么?

代码如下:
#include
using namespace std;
class Base
{
public :
Base(){}
virtual void fun(){ cout << "from Base :" << endl; }
~Base(){ cout << "from Base destructor:" << endl; fun(); }
int b;
};
class Derive : public Base
{
public:
Derive(){ }
virtual void fun(){cout << "from Derive :" << endl;}
~Derive(){ cout << "from Derive destructor:" << endl; fun(); }
int asdfasd[100][100][100];

};
int main()
{
while(true)
{
Base *p = new Derive;
delete p;
}

return 0;
}
可以看到派生类比基类多了一个非常大的数组,如果这个数组真的没有回收的话,不用2秒钟,系统内存就满了。
这一点已测试。具体就是把delete p;注释掉,内存使用量咔咔往上涨,不到2秒系统强制停止了该进程。
但是如果加上delete p;这句就不会,提请注意的是,这里的析构函数并非虚函数,从输出结果也可以看出,程序只调用了基类的析构函数,并未调用派生类的析构函数,那么显然派生类的数据并没有回收,但是从结果上看,系统内存保持一个定值,根本没有上涨,运行多久都可以。
我当然知道虚析构函数适用于在类内有动态申请内存空间的情况,但是我仍然想不明白这里的、派生类独有的内存到底是怎么回收的。

6个回答

liyuanbhu
liyuanbhu   2016.05.01 21:05
已采纳

"程序只调用了基类的析构函数,并未调用派生类的析构函数,那么显然派生类的数据并没有回收" 这里 “那么显然派生类的数据并没有回收” 是错的。
数据回收与析构函数其实没有必然关系。 分配在堆栈上的数据当运行出作用域时自动删除,比如你代码中的“int asdfasd[100][100][100];”,这个机制与析构函数无关。

分配在堆上的数据(也就是 new 出来的数据)需要通过 delete 来删除,也与析构函数无关。只是大家编程习惯喜欢把 delete 写在析构函数里,所以让你有个错觉是析构函数删除了数据。

总之,析构函数其实就是个普通的函数,无非是编译器替你在需要调用它的地方调用它而已。这个函数在本质上与其他的函数没有任何区别。他也不会替你删除什么数据。

liyuanbhu
liyuanbhu 所有 new 出来的变量都是分配在堆上的,由 delete 操作符负责回收。
一年多之前 回复
liyuanbhu
liyuanbhu 这个asdfasd[100][100][100] 也是分配在堆上的,原文里我错写成了分配在栈上了。。。
一年多之前 回复
liyuanbhu
liyuanbhu asdfasd[100][100][100] 是new Derive 这句由 new 操作符分配的空间,delete p 时收回的。
一年多之前 回复
qq906237201
qq906237201 感谢,很精髓。但是有一个问题,asdfasd[100][100][100]是什么时候超期的,或者派生类对象是什么时候超期的,派生类对象不是在堆上吗,这个怎么超出作用域。还是说这个delete p就通知系统,现在整个派生类对象的内存空间都要回收
一年多之前 回复
ArthurJava
ArthurJava   2016.05.01 18:04

Base *p在其生命范围外会释放;
析构函数的调用是先派生类后基类的

ArthurJava
ArthurJava 回复qq906237201: Base *p = new Derive;时,将Derive *隐喻的转型为Base *,所以delete p;时,不会执行Derive的析构函数
一年多之前 回复
qq906237201
qq906237201 这个是堆上的空间,如何释放?而且派生类析构函数调用的时候有显示
一年多之前 回复
lm_whales
lm_whales   Rxr 2016.05.01 19:47

delete p;时
基类析构函数没执行,你可以看到
cout << "from Base destructor:" << endl; fun(); 等都没有执行
但是由于,基类并没有动态分配资源,所以没有资源泄漏
但是,基类的析构函数没被执行,这只怕仍然不是你想要的

lm_whales
lm_whales 派生类没分配内存,何谈释放,这里只是 new ,delete 起来作用而已,也未必正确
一年多之前 回复
qq906237201
qq906237201 派生类的析构函数没执行,但是内存却释放了
一年多之前 回复
lm_whales
lm_whales 回复qq906237201: 派生类析构函数没执行
一年多之前 回复
qq906237201
qq906237201 输出结果是from Base destructor:from base:
一年多之前 回复
qq906237201
qq906237201 基类的析构函数真的执行了,有输出。
一年多之前 回复
lm_whales
lm_whales 如果派生类的析构函数执行了,基类的析构函数,在派生类析构函数没有异常发生的时候,是始终能够执行的
一年多之前 回复
lm_whales
lm_whales 哦,是派生类的析构函数,没有被执行,如果派生类动态分配资源的话,就会有资源泄漏
一年多之前 回复
ArthurJava
ArthurJava   2016.05.01 22:03

debug一下吧

 int _tmain(int argc, _TCHAR* argv[])
{
    while(true)
    {
        Derive *p_drive = new Derive;
        Base *p = p_drive;
        delete p;
    }
    return 0;
}
lm_whales
lm_whales   Rxr 2016.05.02 12:15

delete p;时
只是执行了基类析构函数,派生类析构函数没执行,
但是你的派生类,并没有申请动态内存,因此也不存在资源泄露
只是析构函数仅仅执行一半,而且是后一半(仅仅执行了基类的析构函数)
如果你的派生类,申请资源的话,没有机会释放,因为派生类自己的析构函数没有机会执行

lm_whales
lm_whales delete [] 的作用是找到全部分配的对象,一个一个析构,所以数组和多态天生就没多少缘分
一年多之前 回复
lm_whales
lm_whales 实际上 delete 需要指针类型的作用,是析构对象。不然的话。象C语言那样,是不需要指针有类型区别的
一年多之前 回复
lm_whales
lm_whales 整个对象,释放只是根据内存地址找出 保存的分配记录,予以回收而已,只有 new [],delete[] 可能会出问题,但具体出不出。还要看实现
一年多之前 回复
qq906237201
qq906237201 虽然类内部没有动态内存,但是整个类对象在动态内存上,如果只释放一部分的话不也是会造成内存泄漏吗
一年多之前 回复
lm_whales
lm_whales   Rxr 2016.05.02 21:55

C++ new,new[] 内存分配,分两步
1)分配内存 //这一步只有内存大小是有用的,按照类型需要的大小分配内存
2)构造对象 //这一步调用构造函数,所有信息都是有用的

C++ delete,delete[] 释放内存,也分两步
1)析构对象 //这一步析构对象,对于delete[] 要按照每个元素大小逐一析构对象,所以 所有信息都是有用的
2)释放内存 //这一步只有对象地址是有用的。

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!