views:

319

answers:

7

Hi all,

Due to the well-known issues with calling virtual methods from inside constructors and destructors, I commonly end up with classes that need a final-setup method to be called just after their constructor, and a pre-teardown method to be called just before their destructor, like this:

MyObject * obj = new MyObject;
obj->Initialize();   // virtual method call, required after ctor for (obj) to run properly
[...]
obj->AboutToDelete();  // virtual method call, required before dtor for (obj) to clean up properly
delete obj;

This works, but it carries with it the risk that the caller will forget to call either or both of those methods at the appropriate times.

So the question is: Is there any way in C++ to get those methods to be called automatically, so the caller doesn't have to remember to do call them? (I'm guessing there isn't, but I thought I'd ask anyway just in case there is some clever way to do it)

A: 

I used a very carefully designed Create() factory method (static member of each class) to call a constructor and initializer pair in the same order as C# initializes types. It returned a shared_ptr to an instance of the type, guaranteeing a heap allocation. It proved reliable and consistent over time.

The trick: I generated my C++ class declarations from XML...

280Z28
+1  A: 

The best I can think of is for you to implement your own smart pointer with a static Create method that news up an instance and calls Initialize, and in its destructor calls AboutToDelete and then delete.

Kim Gräsman
+4  A: 

While there is no automated way, you could force the users hand by denying users access to the destructor on that type and declaring a special delete method. In this method you could do the virtual calls you'd like. Creation can take a similar approach which a static factory method.

class MyObject {
  ...
public:
  static MyObject* Create() { 
    MyObject* pObject = new MyObject();
    pObject->Initialize();
    return pObject;
  }
  Delete() {
    this->AboutToDelete();
    delete this;
  }
private:
  MyObject() { ... }
  virtual ~MyObject() { ... }
};

Now it is not possible to call "delete obj;" unless the call site has access to MyObject private members.

JaredPar
I like that, +1!
Kim Gräsman
Destructor can (and should) be virtual, why go through the extra work?
David Rodríguez - dribeas
@dribeas, I updated it to make it virtual.
JaredPar
This example is not so correct, as MyObjects could not be stored (easily) in standard containers (vector, list, etc.)And instead of Delete() method, override the delete operator for the class.
CsTamas
If you intend MyObject to be derivable (as I suspect from declaring the destructor virtual) constructor and destructor should be declared protected rather than private
Indeera
A: 

Except for JavedPar's idea for the pre-destruction method, there is no pre-made solution to easily do two-phase construction/destruction in C++. The most obvious way to do this is to follow the Most Common Answer To Problems In C++: "Add another layer of indirection." You can wrap objects of this class hierarchy within another object. That object's constructors/destructor could then call these methods. Look into Couplien's letter-envelop idiom, for example, or use the smart pointer approach already suggested.

sbi
+1  A: 

http://www.research.att.com/~bs/wrapper.pdf This paper from Stroustrup will solve your problem.

I tested this under VS 2008 and on UBUNTU against g++ compiler. It worked fine.

#include <iostream>

using namespace std;

template<class T>

class Wrap
{
    typedef int (T::*Method)();
    T* p;
    Method _m;
public:
    Wrap(T*pp, Method m): p(pp), _m(m)  { (p->*_m)(); }
    ~Wrap() { delete p; }
};

class X
{
public:
    typedef int (*Method)();
    virtual int suffix()
    {
        cout << "X::suffix\n";
        return 1;
    }

    virtual void prefix()
    {
        cout << "X::prefix\n"; 
    }

    X() {  cout << "X created\n"; }

    virtual ~X() { prefix(); cout << "X destroyed\n"; }

};

class Y : public X
{
public:
    Y() : X() { cout << "Y created\n"; }
    ~Y() { prefix(); cout << "Y destroyed\n"; }
    void prefix()
    {
        cout << "Y::prefix\n"; 
    }

    int suffix()
    {
        cout << "Y::suffix\n";
        return  1;
    }
};

int main()
{
    Wrap<X> xx(new X, &X::suffix);
    Wrap<X>yy(new Y, &X::suffix);
}
Jagannath
+1 Very interesting article. However This seems to only wrap standard methods not constructors and destructors.
iain
+2  A: 

The main problem with adding post-constructors to C++ is that nobody has yet established how to deal with post-post-constructors, post-post-post-constructors, etc.

The underlying theory is that objects have invariants. This invariant is established by the constructor. Once it has been established, methods of that class can be called. With the introduction of designs that would require post-constructors, you are introducing situations in which class invariants do not become established once the constructor has run. Therefore, it would be equally unsafe to allow calls to virtual functions from post-constructors, and you immediately lose the one apparent benefit they seemed to have.

As your example shows (probably without you realizing), they're not needed:

MyObject * obj = new MyObject;
obj->Initialize();   // virtual method call, required after ctor for (obj) to run properly

obj->AboutToDelete();  // virtual method call, required before dtor for (obj) to clean up properly
delete obj;

Let's show why these methods are not needed. These two calls can invoke virtual functions from MyObject or one of its bases. However, MyObject::MyObject() can safely call those functions too. There is nothing that happens after MyObject::MyObject() returns which would make obj->Initialize() safe. So either obj->Initialize() is wrong or its call can be moved to MyObject::MyObject(). The same logic applies in reverse to obj->AboutToDelete(). The most derived destructor will run first and it can still call all virtual functions, including AboutToDelete().

MSalters
Except when Initialize() is reimplemented in a subclass of MyObject, and I need to call the subclass's implementation, not MyObject::Initialize(). Called from the MyObject constructor, it does not do what I need it to do. (AboutToDelete() has the same problem when called from MyObject::~MyObject())Anyway, the "thing that happens after MyObject::MyObject() returns" is the execution of the subclass constructors... those need to happen before Initialize() runs. The logic is reversed for AboutToDelete(), which needs to run before any subclass destructors run.
Jeremy Friesner
That's obviously not the case here since `new MyObject` directly precedes the call. And your counter-example merely changes names. The most-derived constructor runs last, when all invariants have been established and all virtual functions can be called. That ctor can still call Initialize()
MSalters
The most-derived constructor can't safely call Initialize(), because it can't know for sure that it _is_ the most-derived constructor. It very well could be that another class has subclassed it, and in that case Initialize() would be called too soon.
Jeremy Friesner
That's why each class in such cases offers a protected ctor that doesn't call Initialize().
MSalters
A: 

Haven't seen the answer yet, but base classes are only one way to add code in a class hierarchy. You can also create classes designed to be added to the other side of the hierarchy:

template<typename Base> 
class Derived : public Base {
    // You'd need C++0x to solve the forwarding problem correctly.
    Derived() : Base() {
        Initialize();
    }
    template<typename T>
    Derived(T const& t): Base(t) {
        Initialize();
    }
    //etc
private:
    Initialize();
};
MSalters