在 C++ QObject 子类中调用析构函数之前执行操作

Posted

技术标签:

【中文标题】在 C++ QObject 子类中调用析构函数之前执行操作【英文标题】:Perform operations BEFORE calling destructor in C++ QObject subclass 【发布时间】:2014-09-11 11:17:24 【问题描述】:

我有一个继承 QObject 的类层次结构。

我需要在构造之后(对象完全构造时)和销毁之前(对象仍然完整时)执行一些操作。

构造部分没有问题,因为我可以控制对象的构造,将其构造函数设为私有,并将已经可以执行所有必需操作的创建函数公开。

问题在于析构函数。我或多或少做了同样的事情:隐藏析构函数并提供一个销毁函数,该函数执行所有操作,然后销毁对象。

这里是问题开始的地方:我的类层次结构被用作 QJSEngine 脚本模块的一部分,它拥有所有对象的所有权,到时候,它使用 QObject 的析构函数销毁它们,从而绕过我的析构函数功能。将我的析构函数声明为私有是没有帮助的,因为 QJSEngine 总是可以执行 QObject 的析构函数(顺便说一句,任何将我的指针转换为 QObject 的代码也可以)。

我需要在调用析构函数之前执行这个操作,因为我使用了一些虚函数,所以我需要在开始销毁过程之前执行这个操作,所以虚函数调用不会失败。

有没有办法做到这一点?

我附上一段基本代码,显示我的问题:

class IBase: public QObject 
public:
    template<typename T>
    static IBase * create() 
        IBase * obj=new T;
        obj->afterConstruction();
        return obj;
    

    void destroy() 
        this->beforeDestruction();
        delete this;
    

protected:
    IBase(): fBeforeDestruction(false)
    virtual ~IBase()
        //Try to perform operations, but it is too late....
        if (!fBeforeDestruction)
            doBeforeDestruction();
    

    virtual void doAfterConstruction()
    virtual void doBeforeDestruction()
private:
    bool fBeforeDestruction;
    void afterConstruction() 
        doAfterConstruction();
    

    void beforeDestruction()
        fBeforeDestruction=true;
        doBeforeDestruction();
    
;

class TSubclass: public IBase 
protected:
    TSubclass()
    virtual ~TSubclass()

    virtual void doAfterConstruction()
        qDebug()<<"AfterConstruction";
    
    virtual void doBeforeDestruction()
        qDebug()<<"BeforeDestruction";
    
private:
    friend class IBase;
;

int main(int argc, char *argv[])

    //QObject *obj=new TSubclass() //Compile time error! Nice!
    QObject *obj=IBase::create<TSubclass>();

    delete obj;//Wrong! BeforeDestruction is NEVER shown!!! <---- How to change this behaviour?

    IBase * obj2=IBase::create<TSubclass>();
    //delete obj2;    //Compile time error! Nice!
    obj2->destroy();  //Nice!

编辑:

在一些cmets之后,我不得不补充说我想在析构函数之前做几个操作,原因有两个:

    虚拟调用:在析构函数内部不允许虚拟调用,因为它们不会调用被覆盖的函数,而只会调用当前销毁类中的函数。

    动态转换向下转换:一些要做的事情涉及通过 dynamic_cast 进行转换。析构函数内的 dynamic_cast 向下转换总是失败。

编辑 2:

Ezee 的答案按我的需要工作。这是我的完整代码 sn-p,显示代码,还有一个 dynamic_cast:

template <typename T>
class TAfterConstructionBeforeDestruction: public T 
public:
    ~TAfterConstructionBeforeDestruction() 
        this->beforeDestruction();
    

protected:
    using T::T;
;

class IBase: public QObject 
public:
    //Now it can be public, just as the one in QObject!
    virtual ~IBase()

    template<typename T>
    static IBase * create() 
        //Create a 
        IBase * obj=new TAfterConstructionBeforeDestruction<T>;
        obj->afterConstruction();
        return obj;
    

protected:
    IBase()

    virtual void afterConstruction()
    virtual void beforeDestruction()
;

class TSubclass: public IBase 
public:
    virtual ~TSubclass()

protected:
    TSubclass()

    virtual void afterConstruction()
        qDebug()<<"AfterConstruction";
    
    virtual void beforeDestruction();
private:
    friend class IBase;
;

class TSubclass2: public TSubclass 
public:
    virtual ~TSubclass2()
protected:
    TSubclass2()
    virtual void beforeDestruction()
        qDebug()<<"BeforeDestruction from Subclass2";
        TSubclass::beforeDestruction();
    
;

void TSubclass::beforeDestruction() 
    qDebug()<<"BeforeDestruction";

    TSubclass2 * sub=dynamic_cast<TSubclass2*>(this);
    if (sub) 
        qDebug()<<"We are actually a TSubclass2!";
    


int main(int argc, char *argv[])

    //QObject *obj=new TSubclass() //Compile time error! Nice!
    QObject *obj=IBase::create<TSubclass>();

    delete obj;//Now it works fine!

    IBase * obj2=IBase::create<TSubclass2>();
    delete obj2;    //It is still succeeding to dynamic_cast to TSubclass2 without any problem!

【问题讨论】:

你的类中缺少 Q_OBJECT 宏,为什么还要隐藏析构函数? 这里不需要Q_OBJECT,因为没有信号/槽/属性 您声明您需要在“对象仍然完整时”在销毁之前执行操作 - 为什么您不能在析构函数本身中执行这些操作?对象在退出析构函数之前仍然是“完整的”。 尽管描述很长,但我仍然不明白为什么不能只将破坏代码放在析构函数中。 @Merlin069 和 C.R. 我不能把我的代码放在析构函数中,原因有两个:在析构函数本身中调用虚拟调用(这不会做我想要的,因为它不会调用被覆盖的函数) 并且因为我做了几个 dynamic_casts 向下转换,这在析构函数中失败了。我更新了我的问题 【参考方案1】:

首先,我必须说,从构造函数或析构函数调用虚方法是非常bad practice。

    从IBase 的mose 派生后代的构造函数调用doAfterConstruction()。 从 IBase 的 mose 派生后代的析构函数中调用 doBeforeDestruction()

您可以使用信号/插槽来做同样的事情:

    IBase 中声明一个信号beforeDestroyed()(同时添加Q_OBJECT 宏)。 在IBase 的构造函数中,将此信号连接到插槽doBeforeDestruction(使其成为插槽)。 在 Mose 派生的 IBase 后代的析构函数中发出信号:emit beforeDestroyed()

如果你有很多后代,你可能希望避免在每个构造函数/析构函数中做同样的事情。在这种情况下,您也可以使用模板:

template <class T>
class FirstAndLastCall : public T

public:
  FirstAndLastCall ()
  
    doAfterConstruction();
  
  ~FirstAndLastCall 
  
    doBeforeDestruction();
  


Usage:

IBase* obj2 = new FirstAndLastCall<TSubclass>();

【讨论】:

我知道我不能从构造函数/析构函数调用虚方法。否则我不会问...您使用 FirstAndLastCall 模板的方法非常有趣,因为从最派生的析构函数来看,dynamic_casts(请参阅我的问题更新)确实成功了。我稍后再评论

以上是关于在 C++ QObject 子类中调用析构函数之前执行操作的主要内容,如果未能解决你的问题,请参考以下文章

C++中,子类会继承父类的虚函数表!对于父类的析构函数(虚函数) 也会继承吗?

C++在构造函数中调用最终的虚函数

3. 23 模拟面试

用C++设计一个不能被继承的类(转)

在 cout 之前调用的 c++ 析构函数

C++中派生类的构造函数怎么显式调用基类构造函数?