< More C++ Idioms

Calling Virtuals During Initialization

Intent

Simulate calling of virtual functions during object initialization.

Also Known As

Dynamic Binding During Initialization idiom

Motivation

Sometimes it is desirable to invoke virtual functions of derived classes while a derived object is being initialized. Language rules explicitly prohibit this from happening because calling member functions of derived object before derived part of the object is initialized is dangerous. It is not a problem if the virtual function does not access data members of the object being constructed. But there is no general way of ensuring it.

 class Base {
 public:
   Base();
   ...
   virtual void foo(int n) const; // often pure virtual
   virtual double bar() const;    // often pure virtual
 };
 
 Base::Base()
 {
   ... foo(42) ... bar() ...
   // these will not use dynamic binding
   // goal: simulate dynamic binding in those calls
 }
 
 class Derived : public Base {
 public:
   ...
   virtual void foo(int n) const;
   virtual double bar() const;
 };

Solution and Sample Code

There are multiple ways of achieving the desired effect. Each has its own pros and cons. In general the approaches can be divided into two categories. One using two phase initialization and other one using only single phase initialization.

Two phase initialization technique separates object construction from initializing its state. Such a separation may not be always possible. Initialization of object's state is clubbed together in a separate function, which could be a member function or a free standing function.

class Base {
 public:
   void init();  // may or may not be virtual
   ...
   virtual void foo(int n) const; // often pure virtual
   virtual double bar() const;    // often pure virtual
 };
 
 void Base::init()
 {
   ... foo(42) ... bar() ...
   // most of this is copied from the original Base::Base()
 }
 
 class Derived : public Base {
 public:
   Derived (const char *);
   virtual void foo(int n) const;
   virtual double bar() const;
 };
  • using non-member function
template <class Derived, class Parameter>
std::unique_ptr <Base> factory (Parameter p)
{
  std::unique_ptr <Base> ptr (new Derived (p));
  ptr->init (); 
  return ptr;
}

A non-template version of this approach can be found here. The factory function can be moved inside base class but it has to be static.

class Base {
  public:
    template <class D, class Parameter>
    static std::unique_ptr <Base> Create (Parameter p)
    {
       std::unique_ptr <Base> ptr (new D (p));       
       ptr->init (); 
       return ptr;
    }
};
int main ()
{
  std::unique_ptr <Base> b = Base::Create <Derived> ("para");
}

Constructors of class Derived should be made private to prevent users from accidentally using them. Interfaces should be easy to use correctly and hard to use incorrectly - remember? The factory function should then be friend of the derived class. In case of member create function, Base class can be friend of Derived.

  • Without using two phase initialization

Achieving desired effect using a helper hierarchy is described here but an extra class hierarchy has to be maintained, which is undesirable. Passing pointers to static member functions is C'ish. Curiously Recurring Template Pattern idiom can be useful in this situation.

class Base {
};
template <class D>
class InitTimeCaller : public Base {
  protected:
    InitTimeCaller () {
       D::foo ();
       D::bar ();
    }
};
class Derived : public InitTimeCaller <Derived> 
{
  public:
    Derived () : InitTimeCaller <Derived> () {
		cout << "Derived::Derived()\n";
	}
    static void foo () {
		cout << "Derived::foo()\n";
	}
    static void bar () {
		cout << "Derived::bar()\n";
	}
};

Using Base-from-member idiom more complex variations of this idiom can be created.

Known Uses

This article is issued from Wikibooks. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.