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,