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