TIGSource Forums

Developer => Technical => Topic started by: Mikademus on March 13, 2010, 06:13:28 AM



Title: Virtual inheritance and base member initialisation with DDOI hierarchies
Post by: Mikademus on March 13, 2010, 06:13:28 AM
So I have recently run into an interesting base member initialisation situation, which I do not know if it is Standard behaviour, or MSVC9 (VStudio 2008) that behaves... eh... idiosyncratically.

In short, I instantiate concrete classes through an abstract factory. These are all specialisations of abstract bases. All bases extend a base type, and all concrete instances also extend a concrete base type. The inheritance structure looks roughly like this:

Code:
struct AbstractBase {};
struct A : virtual AbstractBase {}:

struct ConcreteBase : virtual AbstractBase {};
struct Concrete_A : ConcreteBase, A {};

The problem is that all instances need to be decorated with some data members identifying it, simple enums in this case.

Code:
enum ID { nothing, id1, id1 };

struct AbstractBase
{
   ID id;
   AbstractBase() : id(nothing) {}
   AbstractBase(ID id) : id(id) {}
};

struct A : virtual AbstractBase
{
   A() : AbstractBase(id1) {}
}:



struct ConcreteBase : virtual AbstractBase
{
   ConcreteBase() : AbstractBase(id2) {}
};

struct Concrete_A : ConcreteBase, A {};

This design seems reasonable and leads you to think that any instance of Concrete_A will have id == id2. However, it will have id == nothing. The reason is that AbstractBase's specific constructior will never be called, only its default constructor. It it is removed, VC will complain about no default constructor having been found.

The solution to this appears quite counter-intuitive:

Code:
enum ID { nothing, id1, id1 };

struct AbstractBase
{
   ID id;
   AbstractBase(ID id) : id(id) {}
};

struct A : virtual AbstractBase
{
   A() : AbstractBase(id1) {}
}:



struct ConcreteBase : virtual AbstractBase
{
   ConcreteBase() : AbstractBase(id2) {}
};

struct Concrete_A : ConcreteBase, A
{
   // NOTE that Concrete_A calls a distant base class directly!
   Concrete_A : AbstractBase(id2), ConcreteBase(), A() {};
};

So Concrete_A calls AbstractBase directly, even though it directly inherits only from ConcreteBase and A. So the specific constructor of the top-most base class must be referenced explicitly in the most-derived class. This striked me as somewhat strange, and I am not certain if this is Standard behaviour or not, if it is a little-encountered aspect of class design that MS has missed, and if this code is portable (which is the main concern!).

Any takers?


Title: Re: Virtual inheritance and base member initialisation with DDOI hierarchies
Post by: Mikademus on March 13, 2010, 07:07:07 AM
I think I might have found the answer myself: it seems to be the correct (Standard) behaviour.

The C++ FAQ Lite §25.12 states (http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.12)  (I boldfaced the critical section):

Quote
[25.12] What special considerations do I need to know about when I inherit from a class that uses virtual inheritance?

Initialization list of most-derived-class's ctor directly invokes the virtual base class's ctor.

Because a virtual base class subobject occurs only once in an instance, there are special rules to make sure the virtual base class's constructor and destructor get called exactly once per instance. The C++ rules say that virtual base classes are constructed before all non-virtual base classes. The thing you as a programmer need to know is this: constructors for virtual base classes anywhere in your class's inheritance hierarchy are called by the "most derived" class's constructor.

Practically speaking, this means that when you create a concrete class that has a virtual base class, you must be prepared to pass whatever parameters are required to call the virtual base class's constructor. And, of course, if there are several virtual base classes anywhere in your classes ancestry, you must be prepared to call all their constructors. This might mean that the most-derived class's constructor needs more parameters than you might otherwise think.

However, if the author of the virtual base class followed the guideline in the previous FAQ, then the virtual base class's constructor probably takes no parameters since it doesn't have any data to initialize. This means (fortunately!) the authors of the concrete classes that inherit eventually from the virtual base class do not need to worry about taking extra parameters to pass to the virtual base class's ctor.

Basically, any object will always be constructed only once (using the computer science definition of object, that is, a memory area), and since construction goes top-down and virtual hierarchies behave slightly uniquely, it is better for the constructors of abstract bases to take no parameters. Otherwise the most-derived class might have to explicitly specify the constructors to use, and in a worst-case scenario do so for every object in the hierarchy.


Title: Re: Virtual inheritance and base member initialisation with DDOI hierarchies
Post by: Average Software on March 13, 2010, 09:14:56 AM
The rationale behind this is that a virtual base object must appear only once in the final object, so which parent constructor is the correct one to run?  There is no right answer, therefore it is the responsibility of the concrete object to initialize all virtual bases.