代码之家  ›  专栏  ›  技术社区  ›  Clark Gaebel

接口的虚拟析构函数

  •  10
  • Clark Gaebel  · 技术社区  · 15 年前

    接口需要一个虚拟析构函数,还是自动生成一个好的?例如,下面两个代码片段中哪一个最好,为什么?请注意这些是全班的。在Java语言中没有其他方法、变量等,这是一个“接口”。

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

    或者…

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

    由于“不是我要找的”答案而编辑:

    每条路线的确切后果是什么。请不要含糊其辞地回答“它不会被正确摧毁”。请告诉我到底会发生什么。我有点大会呆子。

    编辑2:

    我很清楚“virtual”标记意味着,如果通过指向派生的指针删除析构函数,则不会调用它,但是(我认为)这个问题最终归结为“忽略该析构函数是否安全,因为它真的很小?”

    编辑3:

    我的第二篇编辑完全是错误的和虚假的。请阅读实际聪明人的评论了解更多信息。

    6 回复  |  直到 13 年前
        1
  •  13
  •   Justin Ethier    15 年前

    考虑以下情况:

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

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

        2
  •  7
  •   anon    15 年前

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

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

        3
  •  3
  •   Jordan Lewis    15 年前

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

    例如,

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

    如果在基中没有虚拟析构函数,派生的析构函数将永远不会被调用,因此重要的事情将永远不会发生。

        4
  •  3
  •   Edward Strange    15 年前

    主要回复编辑:

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

        5
  •  2
  •   zildjohn01    15 年前

    通常,析构函数应该是 (1) 公共和虚拟,或 (2) 受保护和非虚拟。

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

    如果有人试图删除案例(2)中的接口指针,他们将得到一个编译时错误。

        6
  •  0
  •   Simon    15 年前

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

    public class Base 
    {
     //...
    }
    
    public class Derived
    {
       int i = 0;
       //...
    }
    
    //...
    
    Base* b = new Derived();
    

    如果没有虚拟析构函数

    delete b;
    

    会导致内存泄漏(整数字段至少4个字节),因为它只会破坏 Base 而不是 Derived . 虚拟性确保派生类也被销毁。您不必在中声明虚拟构造函数 衍生的 ,如果在 基地 .