C++ virtual function from constructor

C++OopClassConstructorVirtual

C++ Problem Overview


Why the following example prints "0" and what must change for it to print "1" as I expected ?

#include <iostream>
struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      std::cout << value() << std::endl;
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
}

C++ Solutions


Solution 1 - C++

Because base is constructed first and hasn't "matured" into a derived yet. It can't call methods on an object when it can't guarantee that the object is already properly initialized.

Solution 2 - C++

When a derived object is being constructed, before the body of the derived class constructor is called the base class constructor must complete. Before the derived class constructor is called the dynamic type of the object under construction is a base class instance and not a derived class instance. For this reason, when you call a virtual function from a constructor, only the base class virtual function overrides can be called.

Solution 3 - C++

Actually, there is a way to get this behavior. "Every problem in software can be solved with a level of indirection."

/* Disclaimer: I haven't done C++ in many months now, there might be a few syntax errors here and there. */
class parent
{
public:
     parent( ) { /* nothing interesting here. */ };
protected:
     struct parent_virtual
     {
         virtual void do_something( ) { cout << "in parent."; }
     };

     parent( const parent_virtual& obj )
     {
          obj.do_something( );
     }
};

class child : public parent
{
protected:
     struct child_virtual : public parent_virtual
     {
         void do_something( ) { cout << "in child."; }
     };
public:
      child( ) : parent( child_virtual( ) ) { }
};

Solution 4 - C++

The question of how it works is a FAQ item.

Summarizing, while class T is being constructed, the dynamic type is T, which prevents virtual calls to derived class function implementations, which if permitted could execute code before the relevant class invariant had been established (a common problem in Java and C#, but C++ is safe in this respect).

The question of how to do derived class specific initialization in a base class constructor is also a FAQ item, directly following the previously mentioned one.

Summarizing, using static or dynamic polymorphism on may pass the relevant function implementations up to the base class constructor (or class).

One particular way to do that is to pass a “parts factory” object up, where this argument can be defaulted. For example, a general Button class might pass a button creation API function up to its Widget base class constructor, so that that constructor can create the correct API level object.

Solution 5 - C++

You should not polymorphically call the virtual methods from constructor. Instead you can call them after construction of object.

Your code can be re written as follows

struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      /* std::cout << value() << std::endl; */
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};
      
int main(void) {
   derived example;
   std::cout << example.value() << std::endl;
}
           

Solution 6 - C++

The general rule is you don't call a virtual function from a constructor.

Solution 7 - C++

In C++, you cannot call a virtual / overriden method from a constructor.

Now, there is a good reason you can do this. As a "best practice in software", you should avoid calling additional methods from your constructor, even non virtual, as possible.

But, there is always an exception to the rule, so you may want to use a "pseudo constructor method", to emulate them:

#include <iostream>

class base {
   // <constructor>
   base() {
      // do nothing in purpouse
   }
   // </constructor>

   // <destructor>
   ~base() {
      // do nothing in purpouse
   }
   // </destructor>

   // <fake-constructor>
   public virtual void create() {
      // move code from static constructor to fake constructor
      std::cout << value() << std::endl;
   }
   // </fake-constructor>

   // <fake-destructor>
   public virtual void destroy() {
      // move code from static destructor to fake destructor
      // ...
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 0;
   }

   public virtual void DoSomething() {
      // std:cout << "Hello World";
   }
};

class derived : public base {
   // <fake-constructor>
   public override void create() {
      // move code from static constructor to fake constructor
      std::cout << "Im pretending to be a virtual constructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-constructor>


   // <fake-destructor>
   public override void destroy() {
      // move code from static destructor to fake destructor
      std::cout << "Im pretending to be a virtual destructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 1;
   }
};

int main(void) {
   // call fake virtual constructor in same line, after real constructor
   derived* example = new example(); example->create();

   // do several stuff with your objects
   example->doSomething();

   // call fake virtual destructor in same line, before real destructor
   example->destroy(); delete example();
}

As a plus, I recommend programmers to use "struct" for only fields structures, and "class" for structures with fields, methods, constructors, ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGiovanni FunchalView Question on Stackoverflow
Solution 1 - C++Sean BrightView Answer on Stackoverflow
Solution 2 - C++CB BaileyView Answer on Stackoverflow
Solution 3 - C++Tanveer BadarView Answer on Stackoverflow
Solution 4 - C++Cheers and hth. - AlfView Answer on Stackoverflow
Solution 5 - C++VinayView Answer on Stackoverflow
Solution 6 - C++Hank GayView Answer on Stackoverflow
Solution 7 - C++umlcatView Answer on Stackoverflow