0

I have a class hierarchy which inherits QObject.

I need to perform some operations after construction (when the object is fully constructed) and before destruction (when the object is still complete).

The construction part is no problem, since I can control the construction of an object, making private its constructor and just making public the creator function which already can perform all the required operations.

The problem comes with the destructor. I have done more or less the same: hiding the destructor and providing a destroyer function which performs all the operations and then destroys the object.

Here is where the problem begins:my class hierarchy is used as part of the QJSEngine scripting module, which takes ownership of all the objects, and when it is time, it destroys them using the QObject's destructor, thus, bypassing my destroyer function. It is of no help declaring my destructors private, since the QJSEngine can always execute the QObject's destructor (and by the way, also can any piece of code which casts my pointers to QObject).

I require to perform this operations before calling the destructor, since I use some virtual functions, so I need to execute this BEFORE beginning the destruction process, so the virtual functions calls will not fail.

Is there a way to achieve this?

I attach a basic code snipped showing my problem:

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!
}

EDIT:

After some comments, I have to add to the question that I want to do several operations before the destructor for two reasons:

  1. Virtual calls: The virtual calls are not allowed inside the destructor, since they will not call the overriden functions, but only the functions in the current destroying class.

  2. Dynamic casts downcasting: Some of the things to do involve donwcasting via dynamic_cast. dynamic_cast downcasting inside the destructors always fails.

EDIT 2:

The answer of Ezee works as I needed. Here is my complete code snippet, showing the code, with also a 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!
}
LoPiTaL
  • 2,495
  • 16
  • 23
  • you are missing the Q_OBJECT macro in your classes, also why hide the destructor? – ratchet freak Sep 11 '14 at 11:29
  • Q_OBJECT is not needed here because there are no signals/slots/properties – Ezee Sep 11 '14 at 12:10
  • 1
    You state you need to perform operations before destruction "when the object is still complete" - why can't you perform these operations in the destructor itself? The object is still 'complete' until it exits the destructor. – TheDarkKnight Sep 11 '14 at 12:15
  • Despite a long description, I still don't understand why you cannot just put the code of destruction in, say, the destructor. – Siyuan Ren Sep 11 '14 at 12:33
  • @Merlin069 and C.R. I cannot put my code in the destructor for two reasons: the virtual call being called in the destructor itself (which will not do what I want, since it will not call the overriden functions) and because I do several dynamic_casts downcasting, which in the destructor fails. I updated my question – LoPiTaL Sep 11 '14 at 13:15

1 Answers1

0

First of all, I must say that calling virtual methods from a constructor or a destructor is a very bad practice.

  1. Call doAfterConstruction() from the constructor of the mose derived descendant of IBase.
  2. Call doBeforeDestruction() from the destructor of the mose derived descendant of IBase.

You can do the same using signals/slots:

  1. Declare a signal beforeDestroyed() in IBase (add Q_OBJECT macro also).
  2. In the constructor of IBase connect this signal to a slot doBeforeDestruction (make it a slot).
  3. In the destructor of the mose derived descendant of IBase emit the signal: emit beforeDestroyed().

If you have a lot of descendants you may want to avoid doing the same thing in every constructor/destructor. In this case you can use a template also:

template <class T>
class FirstAndLastCall : public T
{
public:
  FirstAndLastCall ()
  {
    doAfterConstruction();
  }
  ~FirstAndLastCall 
  {
    doBeforeDestruction();
  }
}

Usage:

IBase* obj2 = new FirstAndLastCall<TSubclass>();
Community
  • 1
  • 1
Ezee
  • 4,214
  • 1
  • 14
  • 29
  • I know I cannot call virtual methods from constructor / destructor. Otherway I would not have asked... Your approach with the FirstAndLastCall template is pretty interesting, since from the most derived destructor, dynamic_casts (see my question update) does succeed. I will comment back later – LoPiTaL Sep 11 '14 at 13:26