【问题标题】:Calling virtual method from destructor - workaround?从析构函数调用虚拟方法 - 解决方法?
【发布时间】:2012-02-02 15:28:39
【问题描述】:

我需要为基类Base 的所有后代声明终结方法finalize(),这应该在销毁期间调用,我的意图是从~Base() 调用纯virtual void Base::finalize() = 0,但c++ 禁止这样一个东西。所以我的问题是

我们怎样才能迫使后代以正确和初步定义的方式完成一些最终工作?

该代码无法编译:

#include <QDebug>
class Base {
        public:
            Base(){}
            virtual ~Base(){
                qDebug("deleting b");
                finalize();
            }
            virtual void finalize() = 0;

    };

class A : public Base
    {
    public:
        A(){}
        ~A(){}
        void finalize(){qDebug("called finalize in a");}
    };

    int main(int argc, char *argv[])
    {
        Base *b = new A;
        delete b;
    }

如果我使Base::finalize() 不是纯虚拟的,它会从~Base() 调用而不调度给孩子,因为它已经被破坏了。

我可以从孩子的析构函数中调用 finalize() 但问题是如何强制这样做。换句话说,我的问题是:是否有可能强制编写 Base 类的后代的人使用 finalizing 方法,嗯,以另一种方式而不是在文档中评论它? :)

【问题讨论】:

    标签: c++ inheritance


    【解决方案1】:

    析构函数是释放获取资源的正确位置,但每个类都负责释放自己的资源。 class A 获得的资源不应该(也不能)被 class Base 释放。

    定义虚拟析构函数允许在删除指向class Base 的指向class A 对象的指针时调用class A 的析构函数

    Base* p = new A;
    delete p; // Both A and Base destructors are sequencially called! 
    

    因此,要实现适当的资源释放,您只需在其自己的析构函数中释放每个类的资源。

    【讨论】:

    • 好的,我明白了,但我的问题是:是否有可能强制编写 Base 类的后代的人使用 finalizing 方法,而不是在文档中评论它? :)
    • 这是可能的,但您可能会遇到编译器或运行时错误,因为当Base 的析构函数被调用时,A 的析构函数已经被调用所以没有@ 987654330@ 对象,在其上调用所谓的finalize() 方法。
    • 我使用了您的示例代码并为Base 定义了finalize(),从而使其成为纯虚拟(非纯)。它可以编译,但看看你从它的执行中得到了什么ideone.com/opDXc
    • 是的,这就是我要说的——它从基类调用 finalize,没有调度(正如预期的那样)到 A,因为它已经被破坏了))
    【解决方案2】:

    这就是虚拟析构函数的用途:

    class A
    {
    public:
       virtual ~A() {}
    };
    
    class B : public A
    {
    public:
       virtual ~B() {}
    };
    

    B 类型的对象被销毁时,无论是从指向B 的指针还是指向A 的指针,都会调用这两个析构函数。首先是B::~B(),然后是A::~A()

    【讨论】:

    • @DmitryKachko 只需在您希望 unsubmit 发生的每个析构函数中编写对 unsubmit 的调用
    • 我当然可以,但我不能强迫那些将 A 类用作基础的人
    • 感谢您的回答!它对我有什么帮助?我有 void unsubmit() 函数,应该在销毁期间调用 A 的每个后代。取消提交的实现对于它们中的每一个都是不同的。 ——
    • 如果实例属于该析构函数所在的确切基类,则您可以使用 typeid 仅在虚拟析构函数中执行 unsubmit()。但这似乎是一个丑陋的解决方案。
    • @DmitryKachko 所以在每个后代的析构函数上调用 unsubmit。那有什么问题?在 C++ 中,一分钱一分货。
    【解决方案3】:

    使用主体将基类析构函数设为纯虚函数,这应该完全符合您的要求。

    【讨论】:

    • 强制派生类声明和实现自己的析构函数。
    • 哦,我没有看到基础是抽象的(也许以后会编辑)。
    【解决方案4】:

    为什么不使用 Base 的析构函数?析构函数就是为此而生的。

    搜索 RAII 并发现 c++ 中最好的东西之一。

    大多数习惯使用其他语言的人都必须发现这一点。 c++ 中的资源管理与大多数其他计算机语言完全不同

    【讨论】:

    • RAII 跟他的问题有什么关系?
    猜你喜欢
    • 2016-02-29
    • 2017-08-13
    • 1970-01-01
    • 2016-10-15
    • 2012-09-02
    • 1970-01-01
    • 2010-11-12
    • 2012-09-21
    • 2019-01-30
    相关资源
    最近更新 更多