为什么没有虚方法的基类不需要虚拟析构函数?

时间:2014-06-05 06:47:17

标签: c++

我一直在想,为什么只有虚拟方法的基类需要虚拟析构函数? 看看这段代码(阅读评论):

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,为什么导出的类部分没有泄漏呢?

5 个答案:

答案 0 :(得分:2)

你问:

  

为什么派生类部分没有泄漏?

致电时

delete pb;

运行时知道为对象分配了多少内存并释放它。 delete首先调用对象的析构函数,然后在对象的地址释放内存。堆管理器知道有多少空间。

在这种情况下,pb指向的对象将被删除,因此Base::xDerived::y的空间将被删除。

如果Derived::~Derived()负责释放内存,您将遇到内存泄漏。

答案 1 :(得分:2)

问题的前提,&#34;只有具有虚方法的基类需要虚拟析构函数&#34;,这是非常错误的。使用虚拟析构函数与在类中具有(其他)虚拟方法无关。更正确的准则是对基类使用虚拟析构函数,需要通过指向基类的指针(或引用)来销毁它。

正如其他人指出的那样,您的示例不会泄漏,因为您没有派生特定的分配,因此派生的析构函数是无操作的。一旦开始在Derived构造函数中进行分配,就会出现真正的泄漏。

答案 2 :(得分:1)

是否涉及虚拟方法并不重要。派生类是否可以分配资源很重要。如果他们这样做,你将有内存泄漏。

如果Derived使用了new,那么无论方法是否为虚拟,都会导致泄密。

正如Oli在评论中所说的那样,仅删除“对象的一部分”会导致UB,因此每当您怀疑可能需要通过指向base的指针调用派生对象的析构函数时,将析构函数声明为虚拟是一种好习惯。类。

除非你使用RTTI,否则你基本上不知道指向Base的指针是指向Base实例还是Derived。这就是为什么声明析构函数几乎是一种“首选”方法。

答案 3 :(得分:0)

手头有两个问题:

  1. 为什么给定示例中的派生类不会导致内存泄漏?
  2. 为什么没有虚方法的基类不需要虚拟析构函数?
  3. 对于问题#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