4

Say we have the classic multiple inheritance schema:

class Base {
    int baseMember;
};

class A : public Base {
    int aMember;
};

class B : public Base {
    int bMember;
};

class Derived : public A, public B {
    int derivedMember;
};

This will lay out Derived objects in memory like this:

  • Base's fields <-- (Base*)(A*)this | (A*)this | this
  • A's fields
  • Base's fields <-- (Base*)(B*)this | (B*)this
  • B's fields
  • Derived's fields

To prevent Base's fields from being duplicated, the default method is to inherite Base virtually from A and B:

class A : public virtual Base {
...
class B : public virtual Base {

This will cause the following layout:

  • Offset to Base's fields <-- (A*)this | this
  • A's fields
  • Offset to Base's fields <-- (B*)this
  • B's fields
  • Derived's fields
  • Base's fields <-- (Base*)this | (Base*)(A*)this | (Base*)(B*)this

This solves the problem, with the tradeoff that you have to apply the offset to access Base's fields (and virtual functions) from the other classes.

But why is the following not possible?

  • Base's fields <-- (Base*)(B*)this | (Base*)(A*)this | (A*)this | this
  • A's fields
  • Offset to Base's fields <-- (B*)this
  • B's fields
  • Derived's fields

This would allow A and Derived to access Base without overhead, with no duplication and even shirinking the size by 1 integer. However, if I try it, the compiler still duplicates Base's fields (puts them behind B's fields).

note: I know similar questions have been asked, but I've not seen anything about why this isn't possible.

EDIT: Looking back at it 4 years later, it might have something to do with the fact that the "Offset to Base's fields" in Derived would have to be negative.

Black Mantha
  • 1,147
  • 8
  • 11
  • 1
    You need to lay out A and B before you ever see Derived. Once you choose the layout, it is set in stone. Derived and all other classes must use it for their A and B subobjects. – n. m. could be an AI Jan 10 '16 at 05:41

1 Answers1

3

You need to lay out A and B before you ever see Derived. Once you choose the layout, it is set in stone. Derived and all other classes must use it for their A and B subobjects. (The virtual base position can vary for each complete class and is not considered a part of the layout).

Now, how A may be laid out in the case of virtual inheritance? It must contain the offset-to-Base field, because we don't know how A will be used in other parts of the program. It may be the first base class of some not-yet-written derived class, or the seventh. But functions that use A must have a way to convert A to Base now, without waiting for these derived classes to be defined.

The same of course is true about B.

So both A and B contan their own offsset-to-Base fields. Derived is not in a position to decide otherwise because it's potentially not the only class that inherits A or B.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • A doesn't need the offset field. You can set the rule that Base's fields are always before A's fields, as normal for regular inheritance. You just can't do that for B as well without causing trouble in Derived. – Black Mantha Jan 10 '16 at 15:13
  • "A doesn't need the offset field". Wrong. "You can set the rule that Base's fields are always before A's fields" No you cannot. Why do you think it's possible for A and not for B? The compiler doesn't have a reason to prefer one over the other. – n. m. could be an AI Jan 10 '16 at 15:36
  • When I said "A doesn't need the offset field", I meant in the hypothetical memory layout at the bottom of my question. I know you can't get c++ to do this. I wanted A to inherit non-virutal (so it doesn't have an offset field), B to inherit virtual, and in Derived for B's offset to point to A's Base part. – Black Mantha Jan 10 '16 at 20:54
  • So you want to *explicitly* specify A to inherit non-virtually, B to inherit virtually, and Derived to merge A's Base and B's Base. That's not technically impossible. C++ could have allowed such inheritance, but it doesn't. OTOH if you want to still specify virtual inheritance for A, but want the compiler to automatically laid out A as if it were non-virtual, then no, it's impossible. – n. m. could be an AI Jan 10 '16 at 21:06