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

Perform operations BEFORE calling destructor in C++ QObject subclass

本文关键字:执行 操作 析构函数 调用 C++QObject 子类      更新时间:2023-10-16

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

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

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

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

这就是问题的开始:我的类层次结构被用作QJSEngine脚本模块的一部分,该模块拥有所有对象的所有权,当时间到了,它会使用QObject的析构函数来销毁它们,从而绕过我的destroyer函数。声明我的析构函数为私有是没有帮助的,因为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!
}

编辑:

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

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

  2. 动态强制降频:要做的一些事情涉及通过Dynamic_cast进行降频。析构函数内部的dynamic_cast下转换总是失败。

编辑2:

Ezee的答案符合我的需要。这是我的完整代码片段,显示了代码,还有一个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!
}

首先,我必须说,从构造函数或析构函数调用虚拟方法是一种非常糟糕的做法。

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

您可以使用信号/插槽进行同样的操作:

  1. IBase中声明信号beforeDestroyed()(同时添加Q_OBJECT宏)
  2. IBase的构造函数中,将该信号连接到槽doBeforeDestruction(使其成为槽)
  3. 在IBase的mose派生子代的析构函数中发出信号:emit beforeDestroyed()

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

template <class T>
class FirstAndLastCall : public T
{
public:
  FirstAndLastCall ()
  {
    doAfterConstruction();
  }
  ~FirstAndLastCall 
  {
    doBeforeDestruction();
  }
}
Usage:
IBase* obj2 = new FirstAndLastCall<TSubclass>();