Multiple Inheritance in C++

Download Report

Transcript Multiple Inheritance in C++

CS 3370

C++ allows multiple implementation
inheritance
 Handy for multiple “is-a” situations
 Handy for reusing implementation without “is-a”

Leads to C++’s Darkest Corner (IMHO)
 Inheritance “hierarchy” becomes a DAG
 Ambiguities galore!
 Complicated rules
Invented with Common Lisp (“Flavors”)
 Clients inherit implementation to gain a capability

 Printable, Storable, etc.

Mixin classes are often abstract
 Provides most but not all of feature
 You override virtual function(s) to complete the feature
 A Mixin that itself happens to derive from another class
should do so via virtual inheritance (described later)

See AbleTest.cpp

Suppose we have the proverbial Shape
hierarchy
 Circle, Triangle, etc.


If there is common code to all shapes, you
can put it in Shape
If it is common to only some, keep it a secret
 Don’t have it derive from Shape

See next slide…
class Shape {…};
class Common {…};
class Logo : public Shape {…};
class Circle : public Shape,
protected Common {…};
class Triangle: public Shape,
protected Common {…};

A derived object has a subobject for each base
class
 data duplication!



Name clashes possible
Possible duplicate data via “diamond”
inheritance
Examples: upcast.cpp, delta.cpp,
ambiguous.cpp
Base1
Base1
Base2
Base3
Derived

The derived class holds a pointer to the base
subobject
 It is not inherited by value
 Only one subobject exists in the complete object

Therefore, there is no ambiguity in casting to
the top-level class
Base1
Base2
Base3
Derived


There must be no ambiguity in name lookup
If multiple base classes contain the same
member names, you have a problem
 If a member function, you can override it and do
the Right Thing
 If a data member, you must use “::”


And they must still be accessible
Examples: ambiguous2.cpp, disambiguate.cpp

A fancy name for the way virtual functions
work
 The “most-derived” binding applies
 B::f dominates A::f if A is a (direct or indirect) base
class of B

Non-virtual functions work the same way for
consistency
A{f}
B{f}
C
D
What if a D object calls f( ) via a pointer?



How do you initialize a virtual base?
With diamond inheritance, there are multiple
paths to the shared base
Which intermediate class is responsible?

None!
 There’s no criterion for choosing

It is always initialized, therefore, by the most
derived class
 Other initializations are ignored, but must be
supplied by every derived concrete class


This makes for weird, but necessary code
See next slides
Top
Right
Left
Bottom

All virtual bases are initialized before nonvirtual bases
 No matter where they are
 In top-down-left-to-right order

Subobjects are never initialized twice
 So the compiler has a lot of work to do to keep
track of things
A
B
C
virtual
virtual
virtual
virtual
E
F
G
What is the order of
initialization for G g; ?
(See virtinit2.cpp)
D
Affects copy constructors and assignment operators
 Copy constructors work okay

 Most-derived class must still take care of virtual base,
similar to initialization example

Assignment operators are different!
 They are not inherited
 You must control all sub-assignments explicitly!

Examples: assignment.cpp, virtassign*.cpp