310 likes | 812 Views
CS 3370. Multiple Inheritance in C++. Multiple Base Classes. 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!
E N D
CS 3370 Multiple Inheritance in C++
Multiple Base Classes • 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
Mixin Classes • 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
Another MI ExampleInheriting Implementation “secretly” • 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…
Stroustrup Example class Shape {…}; class Common {…}; class Logo : public Shape {…}; class Circle : public Shape, protected Common {…}; class Triangle: public Shape, protected Common {…};
MI Issues • 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
Virtual Inheritance • 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
Ambiguous Names • 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
Dominance • A fancy name for the way virtual functions work • The “most-derived” binding applies • B::fdominatesA::f if A is a (direct or indirect) base class of B • Non-virtual functions work the same way for consistency
dominance.cpp What if a D object calls f( ) via a pointer?
The Dark Corners • How do you initialize a virtual base? • With diamond inheritance, there are multiple paths to the shared base • Which intermediate class is responsible?
Initializing Virtual Bases • 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
Order of InitializationThe Final Story (Really!) • All virtual bases are initialized before non-virtual 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
virtinit2.cpp What is the order of initialization for G g; ? (See virtinit2.cpp)
Inheritance and Copy Semantics • 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