1
votes

Consider this code:

class X { public: int i; }; 
class A : public virtual X   { public: int j; }; 
class B : public virtual X   { public: double d; }; 
class C : public A, public B { public: int k; }; 
// cannot resolve location of pa->X::i at compile-time 
void foo( const A* pa ) { pa->i = 1024; } 

main() { 
   foo( new A ); 
   foo( new C ); 
   // ... 
} 

In the book "Inside C++ object model", it is said that:

the compiler cannot fix the physical offset of X::i accessed through pa within foo(), since the actual type of pa can vary with each of foo()'s invocations.

So, the compiler has to create something like this:

// possible compiler transformation 
void foo( const A* pa ) { pa->__vbcX->i = 1024; } 

If the program has a pointer to the virtual base class, how can't it resolve the memory address of that member at compile time? As far as I know, when each derived class object is created, the memory layout of each object consists of:

  • all members in the base class
  • a virtual pointer (of a virtual destructor)
  • a pointer to the virtual base class of the derived object
  • all of the members of the derived class object.

So, finding the base class member should simply be finding the right offset from the starting address of the derived class object. But why can't it be resolved?

1

1 Answers

1
votes

The point is that the base class X is virtual with respect to A, so A doesn't yet know who the actual base class will be. The actual base subobject will only be determined by the ultimate derived object, C in this case. In other words, the actual base subobject depends on the dynamic type of *p and cannot be decided statically.

Imagine you had another class C2 : A, B, A2, B2, where A2 and B2 are similar. You could also call foo with a pointer to a C2, and now the base X might be somewhere else.