As we have discussed in the post - c++ type of inheritance, we have covered private inheritance (a.k.a ) implementation inheritance, protected inheritance and public inheritance (a.k.a type inheritance). In this topic we are going to discuss virutal inheritance.
?
However, called virtual inheritance, it does not have close relationship with the private/protected/public inheritance. virtual inheritance is often used in the cases where multiple inheritance hierarichy is used.?
?
?
For our discussion, let's first see an class hierarchy.?
?
?
hierarchy_example 1 ZooAnimal Endangered / \ / -V- -V- / / \ / Bear Racoon / \ \ / ------------\ \ / \ \ --------- / \ \ / ------ Panda
?
ZooAnimal is a virtual base class of Raccon and Bear.?
?
Before we dive into the virtual inheritance, let's first examine the problem without virtual base/inheritance.
?
?
?
C++ is a language which supports multiple inheritance. We have learned that?when a class subclass another class, the super class become a subobject of the derived class, suppose?if through multiple inheritance tree, a class appears twice on both side, then it has some problem?this has two implications
? 1. it waste spaces
? 2. a more serious problem is ambiguity, which member method/data object to call?
?
virtual inhertiance is just to address the issue we have identified earlier. ?under virtual inheritance:??there is only a single, shared base class subobject is inherited regardless of how many times the base class occurs within the derivation hierarchy.
?
so the importance of virtual inheritance under multiple inhertiance is said, let see some code examples.?
?
?
/* * ZooAnimal to support a void paramter constructor */ class ZooAnimal { public: ZooAnimal(string name, bool onExhibit, string fam_name) : _name(name), _onExhibit(onExhibit), _fam_name(fam_name) {} // We will see under what situation we need a void parameter ctor // ZooAnimal() : _name(NULL), _onExhibit(false), _fam_name(NULL) {} virtual ~ZooAnimal(); virtual ostream& print (ostream &) const; string name() const { return _name; } string family_name() const { return _fam_name; } private: protected: bool _onExhibit; string _name; string _fam_name; } ;? then the Bear?
class Bear: public virtual ZooAnimal { public: enum DanceType { two_left_feet, macarena, fandango, waltz } ; Bear(string name, bool onExhibit= true) : ZooAnimal(name, onExhibit, "Bear") , _dance(two_left_feet) {} virtual ostream & print(ostream& ) const; void dance(DanceType ); private: protected: DanceType _dance; };?then The Raccoon
class Raccoon : virtual public ZooAnimal { public: Raccoon(string name, bool onExhibit = true) : ZooAnimal(name, onExhibit, "Raccoon"), _pettable(false) {} virtual ostream& print(ostream& ) const; bool pettable() const { return _pettable; } void pettable(bool petval) { _pettable = petval; } private: protected: bool _pettable; };? followed by Endangered
class Endangered { public: enum CriticalLevel { critical, high, low, trivial }; enum EndangeredCause { environment, population } ; Endangered(EndangeredCause cause, CriticalLevel level) : _cause(cause), _level(level) { } private: protected: CriticalLevel _level; EndangeredCause _cause; };?Last is the Panda?
class Panda : public Bear, public Raccoon, public Endangered { public: Panda(string name, bool onExhibit = true) ; virtual ostream& print(ostream &) const; bool sleeping () const { return _sleeping; } private: protected: bool _sleeping; };?
Panda::Panda(string name, bool onExhibit) : ZooAnimal (name, onExhibit, "Panda"), Bear(name, onExhibit), Raccoon(name, onExhibit), Endangered(Endangered::environment, Endangered::critical), _sleeping(false) { }? We may come back to order of virtual base initialization, but first let's first ?check the code of ctor, Bear and Raccoon just simply pass on/carry the name and onExhibit arguments to its base class when they are serving as the intermediate derived class . So, there is excessive call, a different design, by modifying the ctor of Bear/Raccon is as follow?
class Bear: public virtual ZooAnimal { public: Bear(string name, bool onExhibit= true) : ZooAnimal(name, onExhibit, "Bear") , _dance(two_left_feet) {} protected: // when an intermediate derived class Bear() : _dance( two_left_feet) {} // rest are the same.. };?you may modify the ZooAnimal slight bit to have an empty protected argument list constructor?
class ZooAnimal { protected: ZooAnimal() : _name(NULL), _onExhibit(false), _fam_name(NULL) {} } ;?you can do the same to Raccoon, after the modification, you can modify the ctor of Panda as such .?
Panda::Panda(string name, bool onExhibit) : ZooAnimal (name, onExhibit, "Panda"), Endangered(Endangered::environment, Endangered::critical), _sleeping(false) { }? Neater, isn't it??
/** * hierarchy_example 2 * * Character ZooAnimal ToyAnimal * ^ ^ ^ * | v v * | | / * RockCharacter Bear / * ^ ^ / * | | / * \ / --- - * \ / / * TdeddyBear * */ /* * there is a basic rule in terms the order of the Constructor and Destructor Order * Virtual base classes are always constructed prior to to nonvirutal base classes regardless where they appear in the inheritance hierarchy. * Let's see the example above, we are going to illustrate the discussion we have seen before. * */ class Character {} ; class BookCharacter : public Character { }; class ToyAnimal { } class TeddyBear : public BookCharacter, public Bear, public virtual ToyAnimal { };? The immediate base classes are examined in the order of their declaration for the presence of virutal base calsses.
TeddyBear Paggington;it will call the consturctor as follow.?
ZooAnimal(); ToyAnimal(); Character() BookCharacter() Bear(); TeddyBear()?