接口的虚拟析构函数

时间:2010-06-03 20:09:03

标签: c++ polymorphism

接口是否需要虚拟析构函数,或者自动生成的是否正常?例如,以下两个代码段中哪一个最好,为什么?请注意,这些是整个班级。没有其他方法,变量等。在Java中,这是一个“接口”。

class Base
{
public:
    virtual void foo() = 0;
    virtual ~Base() {}
};

... OR

class Base
{
public:
    virtual void foo() = 0;
    ~Base() {} // This line can be omitted, but included for clarity.
};

编辑因为“不是我想要的”答案:

每条路线的确切后果是什么。请不要给出模糊的答案,例如“它不会被破坏”。请告诉我到底会发生什么。我有点装配书呆子。

编辑2:

我很清楚“虚拟”标签意味着如果通过指向派生的指针删除析构函数将不会被调用,但是(我认为)这个问题最终归结为“省略该析构函数是否安全,因为它真的微不足道吗?“

编辑3:

我的第二次编辑只是完全错误和虚假信息。请阅读实际聪明人的评论以获取更多信息。

6 个答案:

答案 0 :(得分:13)

考虑以下情况:

   Base *Var = new Derived();
   delete Var;

您需要虚拟析构函数,否则当您删除Var时,将永远不会调用派生类的析构函数。

答案 1 :(得分:7)

如果在C ++中通过基类指针删除派生类对象,则结果是未定义的行为。 UB是你真正想要避免的,所以你必须给基类一个虚拟析构函数。引用C ++标准,第5.3.5节:

  

如果操作数的静态类型是   不同于它的动态类型   static类型应该是基类   操作数的动态类型和   静态类型应具有虚拟   析构函数或行为是   未定义。

答案 2 :(得分:3)

如果您希望人们尝试通过父类的指针或引用来删除派生类的对象,则应该使用虚拟析构函数。如果是这种情况,那么在没有虚拟析构函数的情况下,派生类将永远不会被正确销毁。

例如,

Derived::~Derived() { // important stuff }
Base *foo = new Derived();
delete foo;

如果没有Base中的虚拟析构函数,Derived的析构函数永远不会被调用,因此重要的东西永远不会发生。

答案 3 :(得分:3)

主要回复编辑:

没有人能告诉你会发生什么,因为结果是“未定义的行为”。当您通过指向没有虚析构函数的基类的指针删除派生类时,实现可以通过多种方式自由分解。

答案 4 :(得分:2)

一般来说,析构函数应该是(1)公共和虚拟,或(2)受保护和非虚拟。

假设您从未期望任何人通过接口指针删除类实例,受保护的非虚拟析构函数是100%安全的。

如果有人试图删除case(2)中的接口指针,则会出现编译时错误。

答案 5 :(得分:0)

否...不会自动生成虚拟析构函数。您必须在基类中明确声明它们。 但是你不需要为Base的子类声明你的析构函数是虚拟的。这是由编译器完成的。 编译器还将确保以相反的构造顺序(从派生到基础)调用析构函数。

public class Base 
{
 //...
}

public class Derived
{
   int i = 0;
   //...
}

//...

Base* b = new Derived();

如果您没有虚拟析构函数

delete b;

会导致内存泄漏(整数字段至少为4个字节),因为它只会破坏Base而不会破坏Derived。虚拟性也确保派生类也被销毁。您不必在Derived中声明虚拟构造函数,如果您在Base中声明了虚拟析构函数,这将由编译器推断。