我一直在想,为什么只有虚拟方法的基类需要虚拟析构函数? 看看这段代码(阅读评论):
class Base{
private:
int x;
public:
Base():x(0){}
~Base(){
cout<<"Base dtor"<<endl;
}
};
class Derived : public Base{
int y;
public:
Derived():y(0){}
~Derived(){
cout<<"Derived dtor"<<endl;
}
};
int main(){
Derived *pd = new Derived;
Base *pb = pd;
delete pb; // this destroys only the base part, doesn't it?
// so why doesnt the derived part leak?
return 0;
}
我用Valgrind运行它,看到输出是&#34; Base dtor&#34;,并且没有发生内存泄漏。那么,如果只调用基类dtor,为什么导出的类部分没有泄漏呢?
答案 0 :(得分:2)
你问:
为什么派生类部分没有泄漏?
致电时
delete pb;
运行时知道为对象分配了多少内存并释放它。 delete首先调用对象的析构函数,然后在对象的地址释放内存。堆管理器知道有多少空间。
在这种情况下,pb指向的对象将被删除,因此Base::x
和Derived::y
的空间将被删除。
如果Derived::~Derived()
负责释放内存,您将遇到内存泄漏。
答案 1 :(得分:2)
问题的前提,&#34;只有具有虚方法的基类需要虚拟析构函数&#34;,这是非常错误的。使用虚拟析构函数与在类中具有(其他)虚拟方法无关。更正确的准则是对基类使用虚拟析构函数,需要通过指向基类的指针(或引用)来销毁它。
正如其他人指出的那样,您的示例不会泄漏,因为您没有派生特定的分配,因此派生的析构函数是无操作的。一旦开始在Derived
构造函数中进行分配,就会出现真正的泄漏。
答案 2 :(得分:1)
是否涉及虚拟方法并不重要。派生类是否可以分配资源很重要。如果他们这样做,你将有内存泄漏。
如果Derived
使用了new
,那么无论方法是否为虚拟,都会导致泄密。
正如Oli在评论中所说的那样,仅删除“对象的一部分”会导致UB,因此每当您怀疑可能需要通过指向base的指针调用派生对象的析构函数时,将析构函数声明为虚拟是一种好习惯。类。
除非你使用RTTI,否则你基本上不知道指向Base
的指针是指向Base
实例还是Derived
。这就是为什么声明析构函数几乎是一种“首选”方法。
答案 3 :(得分:0)
手头有两个问题:
对于问题#1,答案是你根本不释放派生类的析构函数中的任何内存。如果你这样做了,那么给定示例中的派生类将导致内存泄漏。
对于问题#2,答案是如果你的基类没有声明任何虚方法,那么使用它作为指向派生类的实例的指针真的没什么意义。
<强>更新强>
您似乎误解了内存泄漏一词,它只适用于动态分配的内存:
静态分配的变量(在堆栈或数据部分中)在其声明范围执行结束时自动释放。
动态分配(在堆中)的变量必须“手动”解除分配,以防止内存泄漏。
答案 4 :(得分:0)
why doesn't the derived class part leak?
因为在Derived
构造函数中,您没有使用new
分配内存,请尝试使用下面的代码来查看内存泄漏。它是代码修改后的代码:
#include <iostream>
using namespace std;
class Base{
private:
int x;
public:
Base():x(0){}
~Base(){
cout<<"Base dtor"<<endl;
}
};
class Derived : public Base{
int y;
int *a;
public:
//Derived():y(0){}
Derived()
{
a = new int[10];
y = 10;
cout<<"Derived ctor";
}
~Derived(){
cout<<"Derived dtor"<<endl;
delete a;
}
};
int main(){
Derived *pd = new Derived;
Base *pb = pd;
delete pb; // this destroys only the base part, doesn't it?
// so why doesnt the derived part leak?
return 0;
}
valgrind摘要:
==21686== LEAK SUMMARY:
==21686== definitely lost: 40 bytes in 1 blocks
==21686== indirectly lost: 0 bytes in 0 blocks
==21686== possibly lost: 0 bytes in 0 blocks
==21686== still reachable: 0 bytes in 0 blocks
==21686== suppressed: 0 bytes in 0 blocks