3

I've got a base class that has a virtual function. I want to call that class during the construction because I want the function called for each of the derived classes. I know I can't call a virtual function during construction, but I can't think of an elegant (i.e., avoid repeating code) solution.

What are some work arounds to calling a virtual function during construction?

The reason I want to avoid this is because I don't want to have to create constructors that just call the base class.

class A {
    public:
        A() {
            read();
        }

        // This never needs to be called
        virtual void read() = 0;
}

class B:A {
    public:
        B():A() {   };
        read() { /*Do something special for B here.*/ }

}

class C:A {
    public:
        C():A() {   };
        read() { /*Do something special for C here.*/ }

}

PS: The Python way of doing this is simply to raise NotImplementedError in A::read(). I'm returning to C++ and I'm more rusty than I thought.

jlconlin
  • 14,206
  • 22
  • 72
  • 105
  • 1
    What constraints do you have on the class layout? One way to automate this at least partially would be to add a CRTP class into your class hierarchy. – Benjamin Bannier Apr 25 '14 at 21:02
  • As of right now, I don't have many constraints. I've never heard of CRTP before. I'll check it out. – jlconlin Apr 25 '14 at 21:06
  • You can call a virtual function during construction, but it will be the implementation of the class you whose constructor you are in just then. – Deduplicator Apr 25 '14 at 21:12
  • @Deduplicator Yeah, that won't work. I have to call the method of the calling class, not the class I'm currently in. – jlconlin Apr 25 '14 at 21:13
  • 3
    ?? So, you want to call the function belonging to a derived class in base, before derived is constructed? Bad idea. Or do you want to call a post-constructor, in which case the solution is factory methods? – Deduplicator Apr 25 '14 at 21:14
  • @Deduplicator I suppose what I'm trying to do is call the read method after the object is constructed. But it has to happen every time the derived class is constructed and immediately after construction. – jlconlin Apr 25 '14 at 21:16
  • So, create a valid *zombie*-object in the constructor, and have an `init`-function. Final classes might add the call to `init` add the end of their constructors. – Deduplicator Apr 25 '14 at 21:18
  • It is a good idea to restrict constructors to their only objective: establish the invariant of the object. Any other work is better done in a factory method, e.g. follow some protocol to create a special object. – Jens Apr 25 '14 at 21:22
  • 1
    A good discussion of several approaches to this type of issue: http://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom – aschepler Apr 25 '14 at 21:28

5 Answers5

3

The FAQ perspective.

This is a Frequently Asked Question.

See the C++ FAQ item titled “Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?”.

It’s very often a good idea to check the FAQ (and generally, googling or altavista’ing) before asking.


The question as “Derived class specific base initialization”.

To be clear, while the literal question above is

“What are some work arounds to calling a virtual function during construction?”

it is evident that what’s meant is

“How can a base class B be designed so that each derived class can specify part of what goes on during B construction?”

A major example is where C style GUI functionality is wrapped by C++ classes. Then a general Widget constructor might need to instantiate an API-level widget which, depending on the most derived class, should be a button widget or a listbox widget or whatever. So the most derived class must somehow influence what goes on up in Widget’s constructor.

In other words, we’re talking about derived class specific base construction.

Marshall Cline called that “Dynamic Binding During Construction”, and it’s problematic in C++ because in C++ the dynamic type of an object during class T construction and destruction, is T. This helps with type safety, in that a virtual member function is not called on a derived class sub-object before that sub-object has been initialized, or its initialization has started. But a major cost is that DBDI (apparently) can’t be done in a way that is both simple and safe.


Where the derived class specific init can be performed.

In the question the derived class specific action is called read. Here I call it derived_action. There are 3 main possibilities for where the derived_action is invoked:

  • Invoked by instantiation code, called two-phase construction.
    This essentially implies the possibility of having a mostly unusuable not fully initialized object at hand, a zombie object. However, with C++11 move semantics that has become more common and accepted (and anyway it can be mitigated to some extent by using factories). A main problem is that during the second phase of construction the ordinary C++ protection against virtual calls on uninitialized sub-objects, due to dynamic type changes during construction, is not present.

  • Invoked by Derived constructor.
    For example, derived_action can be invoked as an argument expression for the Base constructor. A not totally uncommon technique is to use a class template to generate most derived classes that e.g. supply calls of derived_action.

  • Invoked by Base constructor.
    This implies that knowledge of derived_action must be passed up to the constructor, dynamically or statically. A nice way is to use a defaulted constructor argument. This leads to the notion of a parallel class hierarchy, a hierarchy of derived class actions.

This list is in order of increasing sophistication and type safety, and also, to the best of my knowledge, reflects the historical use of the various techniques.

E.g. in Microsoft’s MFC and Borland’s ObjectWindows GUI early 1990’ libraries two-phase construction was common, and that kind of design is now, as of 2014, regarded as very ungood.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I appreciate this response. I had seen that FAQ, but didn't quite understand it at first. After reading it a few more times, I think I'm beginning to figure it out. – jlconlin Apr 26 '14 at 03:16
1

One way to achieve this, would be simply to delegate it to another class (that is perhaps a friend) and can be sure to be called when fully constructed.

class A
{
friend class C;
private:
    C& _c; // this is the actual class!    
public:
    A(C& c) : _c(c) { };
    virtual ~A() { };
    virtual void read() = 0;
};


class B : public A
{
public:
    B(C& c) : A(c) { };
    virtual ~B() { };

    virtual void read() { 
       // actual implementation
    };
};


class C
{
private:
    std::unique_ptr<A> _a;

public:
    C() : _a(new B(*this)) { // looks dangerous?  not at this point...
        _a->read(); // safe now
    };
};

In this example, I just create a B, but how you do that can depend on what you want to achieve and use templates on C if necessary, e.g:

template<typename VIRTUAL>
class C 
{
private:
   using Ptr = std::unique_ptr<VIRTUAL>;

   Ptr _ptr;
public:
   C() : _ptr(new VIRTUAL(*this)) {
       _ptr->read();
   };
}; // eo class C
Moo-Juice
  • 38,257
  • 10
  • 78
  • 128
1

This is the factory method approach, putting the factory into the base class:

class A {
public:
    virtual void read() = 0;
    template<class X> static X* create() {X* r = new X;X->read();return X;}
    virtual A* clone() const = 0;
};

class B : public A {
    B():A() {   };
    friend class A;
public:
    void read() { /*Do something special for B here.*/ }
    B* clone() const {return new B(*this);}
};

class C : public A {
    C():A() {   };
    friend class A;
public:
    void read() { /*Do something special for C here.*/ }
    C* clone() const {return new C(*this);}
};

Added a clone-method with covariant return type as a bonus.

Using CRTP:

class A {
public:
    // This never needs to be called
    virtual void read() = 0;
    virtual A* clone() const = 0;
};
template<class D, class B> struct CRTP : B {
    D* clone() {return new D(*this);}
    static D* create() {return new D();}
};

class B : public CRTP<B, A> {
    B() {   };
public:
    void read() { /*Do something special for B here.*/ }
};

class C : public CRTP<C, A> {
    C() {   };
public:
    void read() { /*Do something special for C here.*/ }
};
Community
  • 1
  • 1
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

The workaround is to call the virtual function after construction. You can then couple the two operations (construction + virtual call) in factory function. Here is the basic idea:

class FactoryA
{
public:
    A *MakeA() const
    {
        A *ptr = CreateA();
        ptr->read();
        return ptr;
    }

    virtual ~FactoryA() {}

private:
    virtual A *CreateA() const = 0;
};

class FactoryB : public FactoryA
{
private:
    virtual A *CreateA() const { return new B; }
};

// client code:

void f(FactoryA &factory)
{
    A *ptr = factory.MakeA();
}
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 1
    Addition: Final classes can put the call to `init` into their constructor. – Deduplicator Apr 25 '14 at 21:36
  • @Deduplicator: Can you elaborate? I think this would break the concept, because the call would then happen twice (first in the constructor, then in the factory function). – Christian Hackl Apr 25 '14 at 21:37
  • When you have a leaf class, the constructor can *be* the factory function. – Deduplicator Apr 25 '14 at 21:40
  • Sorry, I don't get it. Do you mean the constructor of the product class or the factory class? – Christian Hackl Apr 25 '14 at 21:42
  • P.S.: obviously, when a class is final, you don't have to worry about subclasses overriding your implementation of the virtual function, but I fail to see how this is compatible with the factory approach. – Christian Hackl Apr 25 '14 at 21:44
  • Actually, there's no need for the factory to be a class. A simple function (probably static template in Base) should be enough. And I meant the derived constructor. – Deduplicator Apr 25 '14 at 21:44
  • So if all concrete product classes are final, then you don't need the factory anymore (for two-step construction). Well, that's true. – Christian Hackl Apr 25 '14 at 21:46
-1

As mentioned by Benjamin Bannier, you can use CRTP (a template which defines the actual read() function.) One problem with that method is that templates have to always be written inline. That can at times be problematic, especially if you are to write really large functions.

Another way is to pass a function pointer to the constructor. Although, in a way, it is similar to calling the function in your constructor, it forces you to pass a pointer (although in C++ you could always pass nullptr.)

class A
{
public:
    A(func_t f)
    {
        // if(!f) throw ...;
        (*f)();
    }
};

class B : A
{
public:
    B() : A(read) {}

    void read() { ... }
};

Obviously, you have the "can't call other virtual functions" problem within the read() function and any function it calls. Plus, variable members of B are NOT yet initialized. That is probably a much worst problem in this case...

For that reason, writing it this way is safer:

   B() : A()
   {
       read();
   }

However, in cases like that, that may be the time when you an some for of init() function. That init() function can be implemented in A() (if you make it accessible: i.e. use public A when deriving) and that function can call all the virtual functions as expected:

class A
{
public:
    void init()
    {
        read();
    }
};

class B : public A
{
public:
    ...
};

I know a lot of people say that an init() function is evil because people who create a B object now need to know to call it... but there isn't much else you can do. That being said, you could have a form of factory, and that factory can do the init() call as required.

class B : public A
{
public:
    static B *create() { B *b(new B); b->init(); return b; }

private:
    B() { ... } // prevent creation without calling create()
};
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • You are really advocating calling a derived function on a base object? **Undefined Behavior** greets you. – Deduplicator Apr 25 '14 at 21:19
  • Deduplicator, I did not mark the read function as a virtual function. – Alexis Wilke Apr 25 '14 at 21:20
  • Calling virtual functions in a partially constructed object is safe, you just must not call pure virtuals nor pass yet unconstructed sub-objects. But calling derived functions on a base which is not actually in that moment a derived is always **Undefined Behavior**, it does not matter how you do it. – Deduplicator Apr 25 '14 at 21:21
  • Also, the real solution is rather the last code sample I propose and not the first. If he wants to do it "the wrong way", there are solutions. Just not what I would do either because it simply isn't safe. – Alexis Wilke Apr 25 '14 at 21:25
  • Even if you advocate a later option, your first one should be right and not UB. – Deduplicator Apr 25 '14 at 21:27
  • 1
    `A(read)` won't compile anyway, unless `B::read` is a static member function. – aschepler Apr 25 '14 at 21:30