Calling a virtual function from a derived class constructor

I have read in many places that it is dangerous to call a virtual function from a constructor. The reason given is that at the point in the constructor where the call occurs, the vtable has not been initialized and the result is undefined (probably a SEGV). But this explanation seems to assume that the call is made from the base class constructor, when the object pointed to by "this" is still incomplete.

What if the call is instead made from a derived class constructor?

A program I've been working on seems to demand this sort of thing in order to set up a dynamically allocated table that will be accessed by methods defined in the Base class, but which must be set up in a way that is specific to each of its derived classes. A bit of example code to make this more concrete: first, the Base class, which is actually derived from OriginalBaseClass, whose constructor calls no virtual functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base : public virtual OriginalBaseClass {
public:
  Base(long int data1 = 4, long int data2 = 1, short int data3 = 365, short int data4 = 1) : data1_(data1), data2_(data2), data3_(data3), data4_(data4), bools_(0) { cout << "In Base constructor\n"; }
  virtual ~Base();
  void Initialize(long int anchor, long int data1, long int data2);
protected:
  virtual bool Rule(long int data, long int anchor, long int data1, long int data2) { return false; }
private:
  bool* bools_;
  long int data1_;
  long int data2_; 
  short int data3_;
  short int data4_;
};


Next, the Base::Initialize() method, which sets up the dynamically allocated table, a boolean array. The Initialize() method calls the virtual function Rule(), which as shown above, is NOT called from the Base constructor:

1
2
3
4
5
6
7
8
void Base::Initialize(long int anchor, long int data1, long int data2)
{
  cout << "In Base::Initialize\n";
  bools_ = new bool[data1];
  if ( bools_ == NULL ) throw "Base::Initialize: Operator 'new' returned null pointer, unable to allocate space for 'bools_' array";
  for ( short int i = 0 ; i < data1 ; i++ )
    bools_[i] = this->Rule(anchor + i, anchor, data1, data2);
}


Finally, a derived class and its Rule function, which does not itself change any data in the table. The derived class calls Initialize() from its constructor, and thus causes its own Rule() function to be called from the constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Derived_A : public Base {
public:
  Derived_A() : OriginalBaseClass(-1), Base(4, 1, 365) { cout << "In Derived_A constructor\n"; Initialize(1, 4, 1); }
protected:
  virtual bool Rule(long int data, long int anchor, long int data1, long int data2);
};
    
bool Derived_A::Rule(long int data, long int anchor, long int data1, long int data2)
{
  if ( (data % 4) == 0 ) {
    return true;
  } else
    return false;
}


Is there anything unsafe about doing this? If so, why? My understanding of C++ could be wrong, but it seems that there should be no problem as long as the virtual function cannot resolve to a member of any class below Derived_A (or any class whose constructor calls Base::initialize()) in the inheritance hierarchy. I believe C++11 has a "final" keyword to signal to the compiler that a method cannot be overridden by descendants, but I am trying to avoid C++11 extensions at this point since I am not too familiar with that version of the language.

Incidentally, this code compiles and runs fine, even with optimization, under both g++ and clang. If, however, Derived_A is defined via an intermediate class Derived_B (which is directly derived from Base and is empty), the code will segfault when compiled -O2 or -O3 with clang. However, see my other thread: the crash still happens when the virtual methods and the attempt to call them are completely removed from the code, so it's apparently a different issue,
Last edited on
I have read in many places that it is dangerous to call a virtual function from a constructor. The reason given is that at the point in the constructor where the call occurs, the vtable has not been initialized and the result is undefined (probably a SEGV)

That's wrong. Calling virtual functions from constructors in C++ is safe and well-defined. There is just one rule that some people find surprising: the dynamic type of the object under construction is the class for which the constructor is running. Nothing further-derived exists in that moment. Calls to any virtual function will dispatch to the type for which the constructor is running or some base (if this type didn't provide an overrider), but never to a further-derived overrider; those don't exist and will never be called. Exactly because doing that would be unsafe.

Other OOP languages (notably, Java, and even D) always perform two-stage initialization: they first create the most-derived object and initialize all data members to some default values, then they run all constructors top to bottom and the virtual calls from the constructors there will dispatch all the way to the most-derived type (which is way more unsafe than the C++ approach if you ask me: those overriders access the default-initialized members for which constructors haven't run)

In any case, you're not doing anything wrong. The concern is that someone who did too much Java may further derive a class FurtherDerived_A : public Derived_A, override Rule in FurtherDerived_A, and complain that the constructor of Derived_A is not calling their further-derived Rule.
Last edited on
Thanks for your reply! What you say makes sense and is consistent with what I've seen happen with test code: if the Base constructor calls Initialize, then the Base version of Rule is the one that gets called even if the object being constructed is of type Derived_A. If Rule is defined as pure virtual in Base, then a runtime error occurs (pure virtual function called). If the call to Initialize is made from Derived_A's constructor, then Derived_A's version of Rule gets called, exactly as expected.

I don't understand why calling virtual functions from constructors is considered so dangerous then. There must be many cases where this kind of derived class-specific initialization is desired and can be easily made to happen by having the derived classes call an inherited initialization method which then uses a virtual function to do the work. Yet the usual recommendation is to create derived objects using a factory or some even less intuitive procedure.

Your point about not allowing a further derived class to override Rule is well-taken, though. In my design, any non-abstract class must be the final class in its inheritance hierarchy and must call the Initialize method, though it may inherit the Rule function from a higher, abstract class. But this is a private code, and no one else is likely to ever try to extend my class hierarchy. In any case, it is heavily commented to remind myself of how everything works, and what is allowed and not allowed in defining new classes.
Topic archived. No new replies allowed.