Diamond of Death

I have spent the past numerous years working on a game engine that avoided the Diamond of Death. This is a phenomena that occurs in languages that support multiple inheritance.

I decided to take a look at how Microsoft Visual C++ implements the solution using virtual inheritance. This is expressed by using “class B : public virtual A”.

A simple characterization is below:

class A
{
protected:
	int value;
public:
	A()
		: value(1)
	{
	}
	virtual void WhereAmI()
	{
		printf("I'm in A (value = %d)!\n", value);
	}
};

class B : public virtual A
{
	int bvalue;
public:
	B()
		: bvalue(2)
	{
		value += 2;
	}
	virtual void WhereAmI()
	{
		printf("I'm in B (value = %d)!\n", value);
	}
};

class C : public virtual A
{
	int cvalue;
public:
	C()
		: cvalue(4)
	{
		value += 4;
	}
	virtual void WhereAmI()
	{
		printf("I'm in C (value = %d)!\n", value);
	}
};

class D : public B, public C
{
	int dvalue;
public:
	D()
		: dvalue(8)
	{
	}

	virtual void WhereAmI()
	{
		printf("I'm in D (B::value = %d)!\n", B::value);
		printf("I'm in D (C::value = %d)!\n", C::value);
	}
};

An instance of D will first construct a single instance of A, then B, C, and finally D. The instance of A will be shared between B & C so the final value in A.value will be 7. In fact the output of the print statements in D will both be 7 since the instance of A is shared.

Constructing an instance of D requires a bit more information. The initialization sequence is:

  • The vtables for B & C are set up in memory at their respective offsets
  • The constructor for A is called
    • A’s initial vtable is established
    • A’s data is initialized
  • The constructor for B is called
    • Constructor call to A is skipped
    • Initialization state for A is updated
    • A’s vtable is updated
    • B’s data is initialized
  • The constructor for C is called
    • Constructor call to A is skipped
    • Initialization state for A is updated
    • A’s vtable is updated
    • C’s data is initialized
  • D is constructed
    • A’s vtable is changed again
    • D’s data is initialized

The layout of D in memory is (based on the above example, 32 bit executable):

Field Offset
B’s vtable 0
B’s data +4
C’s vtable +8
C’s data +12
D’s data +16
A’s init state +20
A’s vtable +24
A’s data +28

A’s class information follows the data for any class that inherits from it virtually.  The vtable entry for A in B’s vtable (in D) has an offset of 24 (0x18) pointing to the start of A’s vtable.

The final in memory representation is:

9c 77 d8 00 02 00 00 00 00 7b d8 00 04 00 00 00  œwØ......{Ø.....
08 00 00 00 00 00 00 00 98 77 d8 00 07 00 00 00  ........˜wØ.....

Even looking at an instance of a B shows the same pattern:

Field Offset
B’s vtable 0
B’s data +4
A’s init state +8
A’s vtable +12
A’s data +16

With an offset from B’s vtable to A of 12 (0xc).

So you get the benefits of having a single instance of A with the added complexity of additional vtables, initialization state, and having to evaluate the location of A through a vtable to resolve queries against A.

Final question: is it is possible (and is there value) in being able to create custom vtables during execution to point to a virtual instance of a class much farther away in memory?  While it would create bloat for duplicating the vtable it might provide interesting functionality/flexibility.

Leave a Reply

Your email address will not be published. Required fields are marked *