C++ – How to avoid two step initialization (C++)

cdesign-patterns

I'd like to follow the RAII(resource acquisition is initialization) idiom throughout my code but I'm also doing the template pattern where I'm developing generic versions of my classes and using them to build a common codebase for certain things. Sometimes I need to enforce an initialization sequence where I would need to call the specialized object's virtual functions in the constructor but that's not possible in C++. The only solution I can think of is a two step initialization by calling an init function after the object is created but that breaks the RAII idiom. Is there any solution to this?

#include <memory>

class A {
public:
    A() {
        // I want to call B's foo() here
    }
    virtual void foo() = 0;
};

class B : public A {
public:
    B() {};
    virtual void foo() {};
};

void main() {
    std::unique_ptr<A> a(static_cast<A*>(new B));

    // Use b polymorphically from here...
}

Best Answer

The basic problem is here:

A() {
    // I want to call B's foo() here
}

Why do you want to call B's foo() here? How can something specific to B be relevant to the initialization of A, which has no idea at all about B? The answer will just about always point to a poor design.

One class of answers (quite commonly encountered, but apparently not relevant here) is when a virtual method is used to return a B-specific value that A will store and/or use. The poor design concept here is that a virtual method should be used to pass this value. A superior alternative is to pass the value from the derived class as an argument to the base class constructor. That way the base class doesn't have to worry at all how the value comes about.

Here, however, foo() has a void return signature. Whatever it does, it must do it in the context of B. Then, how can it affect anything about A? It could have access to a protected member, or it could call other base class methods. But why should any of these actions have to occur during A's initialization? Why not during B's initialization?

The basic point is that the initialization of A should be designed to depend on its constructor parameters only. That way, any derived class can achieve its specific customization of the base class by invoking the base class constructor from its own constructor with the appropriate values.

ADDENDUM: For the template method pattern, the best you can do is to enforce the implied discipline, with an organization on these lines:

class Base
{
public:
    virtual ~Base(); // for polymorphic destruction
protected: // to ensure invocation from derived classes only
    Base()
    {
        // pre-customization base class actions
    } 
    void finish_base() // virtual methods can be called from here!
    {
        // post-customization base class actions
    }
};

class Derived : public Base
{
public:
    Derived() : Base()
    {
        // derived class customized actions
        finish_base();
    }
};

This is not "two-phase" inasmuch as the Derived class initialization occurs entirely within its constructor. How that work is split up is internal to the implementation, and thus "unitary" from any outside perspective.

Related Topic