5

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...
}
3
  • Do you have to use inheritance-based polymorphism or would type erasure or even static polymorphism also be an option?
    – 5gon12eder
    Jan 7, 2017 at 8:05
  • Related: stackoverflow.com/questions/962132/…
    – Blrfl
    Jan 7, 2017 at 12:06
  • Is it possible to use lazy evaluation? Perhaps you can have an, e.g., fooCalled flag, so that the class internals can call foo immediately before its result is first needed rather than during initialisation. Aug 11, 2019 at 16:21

4 Answers 4

6

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.

5

You could take the approach of making the constructor of A protected, the constructor of B private, and then make a class static helper to instantiate B; the static helper would then do the two stage initialization; this would ensure that the instantiation is always done correctly, and making the constructor private/protected ensures that you don't accidentally try to instantiate the class directly. This does have the downside that you can't instantiate the class on the stack or as a member -- it has to be on the heap.

4
  • why it can't be on stack? Jan 7, 2017 at 6:49
  • maybe there is a way to do it on the stack, but not like I described, because the static helper needs to return a pointer (remember, the constructor is private, so you can't just instantiate the class). Maybe you could do it with a templated wrapper and a protected constructor...
    – B.J.
    Jan 7, 2017 at 6:58
  • A simple template for the static factory function should do the trick.
    – 5gon12eder
    Jan 7, 2017 at 8:07
  • 2
    class A { A() {}; public: static A instance () { return A (); } }; No heap involved.
    – D Drmmr
    Jan 19, 2019 at 20:08
4

You can't call B's functions from A's constructor, because the B subobject doesn't exist yet.
Any design that'd rely on a virtual function call from a parent constructor is nonsensical.

The solution is simply to call foo() from B's constructor.

1
  • Many years ago Borland Pascal allowed this. It was a disaster because member variables had not been initialised when the virtual method was called. +1 for your answer.
    – kiwiron
    Jan 18, 2019 at 3:55
0

Since C++11 there is a way to do this relatively easy and to enforce the usage as well. One has to do it through 2 classes and the two stage can apply to both construct and destruct. Outside of the usage above, this is also useful in multithreaded apps, when base class wants to subscribe/unsubscribe to events in one of the base classes and the calls might use virtual functions. Below is for illustration:

class TwoWayStructorBase {
    virtual void _use_TwoWayStructor_to_construct_() = 0; // Make sure that the TwoWayStructor wrapper is used if inherited from this class
    using Call = std::function<void()>;
    std::vector<Call> construct;
    std::vector<Call> destruct;

public:
    void init() {for(auto &c : construct) {c();} };
    void destroy() {for(auto &d : boost::adaptors::reverse(destruct)) {d();} };

protected:
    void stageTwoInit(Call c) {construct.push_back(c);}
    void stageTwoDestroy(Call c) {destruct.push_back(c);}
};

template <typename T>
struct TwoWayStructor {
    virtual void _use_TwoWayStructor_to_construct_() {};

public:
    template <class... A>
    TwoWayStructor(A&&... a) : T(std::forward<A>(a)...) {
        auto & base = static_cast<TwoWayStructorBase&>(*this); // in case of being inherited multiple times, this makes sure that virtual inheritance is used
        base.init();
    }

    ~TwoWayStructor() {
        auto & base = static_cast<TwoWayStructorBase&>(*this);
        base.destroy();
    }
};

class A : virtual public TwoWayStructorBase {
    A() {
        stageTwoInit([this](){ 
            this->xI();
        });
        stageTwoDestroy([this](){
            this->xD();
        });
    };
    virtual void xI() = 0;
    virtual void xD() = 0;
};
class B : public A {
    virtual void xI() { };
    virtual void xD() { };
};
int main() {
    TwoStageStructor<B> b;
};

As above if the class is not used as expected, it will not compile.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.