class Bed { public: Bed(); //constructor void sleep(); int weight; }; class Sofa { public: Sofa(); //constructor void sit(); int weight; }; class SleeperSofa: public Bed, public Sofa { public: SleeperSofa(); void foldOut(); }; int main() { SleeperSofa ss; ss.sit(); //Sofa::sit() ss.foldOut(); //SleeperSofa::foldOut() ss.sleep(); //Bed::sleep() return 0; }Notice the class SleeperSofa inherits from both Bed and Sofa. SleeperSofa inherits all members of both base classes. Thus ss.sit() and ss.sleep(); are legal. You can use a SleeperSofa as a Bed or as a Sofa. Plus the class SleeperSofa can have members of its own such as foldOut().
Bed::weight
and another member
Sofa::weight
. Because they have the same name, unqualified
references to weight are now ambiguous as in the following example:
#include <iostream.h> void fn() { SleeperSofa ss; cout << "weight = " << ss.weight //illegal: which weight? << "\n"; }The inheritance structure is shown in the following figure:
lec11sofa1.eps
We could fix this by explicitly specifying the desired base class:
#include <iostream.h> void fn() { SleeperSofa ss; cout << "weight = " << ss.Sofa::weight //ok but ugly << "\n"; }This is ok but undesirable because it forces class information to leak outside the class into the application code.
lec11sofa2.eps
#include <iostream.h> //Furniture - more fundamental concept; this class // has "weight" as a property class Furniture { public: Furniture() { } int weight; }; class Bed : public Furniture { public: Bed() { } sleep() { } }; class Sofa : public Furniture { public: Sofa() { } void watchTV() { } }; class SleeperSofa : public Bed, public Sofa { public: SleeperSofa() { } void foldOut() { } }; void fn() { SleeperSofa ss; cout << "weight = " << ss.weight //problem solved; right? << "\n"; } int main() { fn(); return 0; }This doesn't work because now SleeperSofa inherits 2 Furniture objects; one through Bed and one through Sofa. So when weight is called, it doesn't know which one to use. The inheritance structure actually looks like:
lec11sofa3.eps
Obviously SleeperSofa only needs one Furniture object. To accomplish this, we use virtual inheritance.
#include <iostream.h> class Furniture { public: Furniture() { } int weight; }; class Bed : virtual public Furniture { public: Bed() { } void sleep() { } }; class Sofa : virtual public Furniture { public: Sofa() { } void watchTV() { } }; class SleeperSofa : public Bed, public Sofa { public: SleeperSofa() : Sofa(), Bed() { } void foldOut() { } }; void fn() { SleeperSofa ss; cout << "weight = " << ss.weight << "\n"; } int main() { fn(); return 0; }Notice the addition of the keyword virtual in the inheritance of Furniture in Bed and Sofa. This says, ``Give me a copy of Furniture unless you already have one somehow, in which case I'll just use that one.'' So SleeperSofa ends up inheriting only one Furniture object. Now the reference to weight in fn is no longer ambiguous and we have solved the problem of name collisions. Now the inheritance structure is the desired one and looks like:
lec11sofa2.eps
If virtual inheritance solves this problem so nicely, why isn't it the norm or default? There are 2 reasons. First virtually inherited base classes are handled internally quite differently than normally inherited base classes, and these differences involve extra overhead. Second, you might want 2 copies of the base class, though this is unusual. As an example of the latter, consider a TeachingAssistant who is both a Student and a Teacher, both of which are subclasses of Academician. If the university gives its TA's 2 ID's-a student ID and a separate teacher ID-class TeachingAssistant will need 2 copies of class Academician.
#include <iostream.h> class Base1 {int mem}; class Base2 {int mem}; class SubClass: public Base1, public Base2 {}; void fn(SubClass *pSC) { Base1 *pB1 = (Base1*)pSC; Base2 *pB2 = (Base2*)pSC; }pB1 and pB2 are not numerically equal even though they came from the same original value pSC. (Actually, if fn is passed a zero, they are equal. See how strange it gets?)