CS247 Lecture 19
Last Time: LI in SOLID, Multiple inheritance
This Time: More multiple inheritance, ID in SOLID
As such, we cannot use classes with private or protected inheritance polymorphically.
Multiple inheritance CAN have some tricky semantics.
Ambiguous - it’s not clear whether A1::foo
or A2::foo
should be called - b.food()
doesn’t compile.
Disambiguate: b.A1::foo()
or b.A2::fooo();
Question
Can you do this if
b
inherits from a single class, say class B: public A, can you doB.A::foo
?Although not needed, yes you can do this! It’s using the Scope Resolution Operator.
Another tricky aspect of multiple inheritance - shared Base class. “Diamond Problem”, “deadly diamond of death”.
Because a D
is-a B
and a C
, it ends up having 2 a
fields - one from the B
portion of the object and one from the C
portion of the object.
Must instead disambiguate - which a
field are we talking about? The one from the B
portion, or the one from the C
portion?
Wha if we wanted to disable this (somewhat strange) behaviour, and have only copy of A
in our hierarchy?
Solution: Use Virtual Inheritance.
Syntax:
struct B: virtual A{...}
class C: virtual public A{...}
"virtual" inheritance
The keyword “virtual” indicates that this base class will be shared among all others who inherit from it virtually in the inheritance hierarchy.
Now: dObj.a
→ unambiguous, a
field is now shared between both portions of the object.
Constructors for virtual bases must run before any other constructors. (Just remember this.)
Note
In this example, class
A
is virtually inherited by both classesB
andC
. When a class virtually inherits from a base class, the virtual base class is constructed before any non-virtual derived class constructors. This ensures that there is only one instance of the virtual base class shared among all the classes that virtually inherit from it.Why do constructors for virtual bases run before any other constructors? This is because the compiler needs to guarantee that the shared base class is initialized properly and that its members are ready to be used by the constructors of all the classes that virtually inherit from it.
Typically:
Virtual inheritance:
- This shows the order in which the objects is constructed for virtual inheritance.
Virtual inheritance (idk what’s going on…)
- Call’s D constructor first
- D calls A’s constructor
- Return to D
- Call’s B constructor
- Return to D
- Call C
- Return to D look at picture above.
Fixed:
In this corrected version:
- Class
B
andC
are derived virtually fromA
, ensuring that only a single instance ofA
is present in the inheritance hierarchy. - In class
B
andC
, the constructors call the constructor ofA
using their member initialization lists. - In class
D
, the constructor initializes the virtual base classA
explicitly withA{5}
, and the default constructors ofB
andC
are called usingB{}
andC{}
respectively.
This code follows the correct order of constructor execution, ensuring that the virtual base class A
is constructed before the constructors of derived classes (B
and C
) are executed. This maintains the integrity and correctness of the inheritance hierarchy.
Destruction sequence/steps
The destruction steps for the classes
A
,B
,C
, andD
would follow the reverse order of construction, with the destructor of each class being called in the reverse order of their constructor execution. Here’s the order in which the destructors would be called:
- Destructor of class
D
.- Destructor of class
B
(via the virtual inheritance fromA
).- Destructor of class
C
(via the virtual inheritance fromA
).- Destructor of class
A
.
What about object layout with multiple virtual inheritance???
B object:
To treat a B*
like an A*
, simply create a pointer copy, prints at the same location, ignore B
fields. Same strategy cannot be used for multiple virtual inheritance.
C++/clang
:
Note
virtual Base(A) is actually stored at the end of the object! Not the beginning.
If bp
points at a D
object, see above memory diagram.
If bp
points at a B
object,
If w’re pointing at a D
object, then bp->a
is 40 bytes below the pointer address.
If we’re pointing at a B
object, then bp->a
is 24 bytes below the pointer address.
Note
Now, finding superclass fields depends on the dynamic type.
Solution: Store the offset to the superclass fields inside the vtable
.
This is where the name “virtual” inheritance comes from.?
Note
The
D
object does not look like anA
object, aC
object or aB
object simultaneously, but portions of it do.
Doing pointer assignment can change where the pointer is pointing to.
- This is really IMPORTANT, be very careful
static_cast
/ dynamic_cast
will also adjust pointer locations for you as necessary.
reinterpret_cast
won’t - underscores danger associated with the cast.
Finally, both Google C++
guide, and ISOCPP both recommend when using multiple inheritance, interface classes are best.
Interface classes are those that:
- Define all methods as pure virtual
- Contain no state (fields)
Next: CS247 Lecture 20