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()?