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

时间:2014-09-11 11:17:24

标签: c++ qt inheritance

我有一个继承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!
}

编辑:

经过一些评论之后,我必须在析构函数之前添加一些我想要进行多次操作的问题,原因有两个:

  1. 虚拟调用:析构函数中不允许进行虚拟调用,因为它们不会调用覆盖函数,而只调用当前销毁类中的函数。

  2. 动态投射转发:有些事情涉及通过dynamic_cast进行转播。析构函数内的dynamic_cast向下转换总是失败。

  3. 编辑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 个答案:

答案 0 :(得分:0)

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

  1. 从IBase派生的后代的构造函数中调用doAfterConstruction()
  2. 从IBase派生的后代的析构函数中调用doBeforeDestruction()
  3. 你可以使用信号/插槽来做同样的事情:

    1. beforeDestroyed()中声明信号IBase(同时添加Q_OBJECT宏)。
    2. IBase的构造函数中,将此信号连接到插槽doBeforeDestruction(将其设为插槽)。
    3. 在IBase派生的IBase后代的析构函数中发出信号:emit beforeDestroyed()
    4. 如果你有很多后代,你可能想避免在每个构造函数/析构函数中做同样的事情。在这种情况下,您还可以使用模板:

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