我们声明一个DancerSinger类,多继承于Dancer和Singer类。Dancer和Singer类继承与People类。
#include <string>
class People{
private:
std::string name;
int age;
public:
People();
virtual ~People();
void show();
};
class Dancer: public People{
private:
std::string skill;
public:
Dancer();
virtual ~Dancer();
void show();
};
class Singer: public People{
private:
std::string skill;
public:
Singer();
virtual ~Singer();
void show();
};
class DancerSinger: public Dancer, public Singer{
public:
void show();
DancerSinger();
virtual ~DancerSinger();
};
在上述的声明中,我们创建一个DancerSinger对象。那么这个对象中包含几个People副本呢?
答案是2个。DancerSinger继承的Dancer和Singer分别指向2个不同的People副本。
冗余的2个People副本,不仅浪费资源,在用基类引用或者指针指向派生类时还会产生二义性。
DancerSinger people1;
People* pp = &people1;
上述的People指针将不知道指向Dancer的People副本还是指向Singer的People副本。所以,我们应该在赋值时采用强制类型转换。
People* pp1 = (Dancer*)&people1;
People* pp2 = (Singer*)&people1;
通过上面的方法,People指针就可以指向唯一的People副本。然而这种方法繁琐且存储冗余的2个副本。我们可以通过使用虚基类来保存唯一的一个基类副本。
虚基类
通过在继承时,使用virtual关键字,我们可以将基类声明为虚基类,在继承时保留唯一的基类副本。Dancer,Singer,DancerSinger的声明改为如下:
class Dancer: virtual public People{};
class Singer: virtual public People{};
class DancerSinger: public Dancer, public Singer{};
通过将Dancer,Singer类的继承声明加上virtual关键字。我们在创建DancerSinger类的对象时,该对象将只拥有一个People副本。
虚基类的构造函数
在使用虚基类时,不允许在DancerSinger的构造函数中,通过调用Dancer和Singer的构造函数来间接初始化People类。因为现在我们只有一个People副本。通过Dancer和Singer构造函数,2条不同的路径来初始化People副本将造成冲突,此时将会调用People的默认构造函数来生成唯一的People副本。如果不想使用默认的People构造函数,我们需要在派生类中显示地调用People的构造函数:
People::People(const People& p){}
Dancer::Dancer(const People& p, string danceSkill){}
Singer::Singer(const People& p, stirng singSkill){}
DancerSinger::DancerSinger(const People& p, string danceSkill, string singSkill):People(p), Dancer(p, danceSkill), Singer(p, singSkill){}
注意:只有在使用虚基类的时候,这样是合法的。但是在非虚基类时,这样的调用是不合法的。
虚基类的实现原理
虚基类和虚函数的实现原理相似,都通过一个隐藏的指针成员和虚表来完成工作。虚基类的指针指向虚表,而虚表中记录着基类成员与指针的地址偏移。所以当使用虚基类指针去访问基类成员时,会先读取虚指针找到虚表,再根据记录的偏移量来找到基类中的成员。所以,当使用虚基类的指针去访问基类成员时,效率要低一些,毕竟要多做一次地址的计算。并且每一个虚基类对象都要额外消耗一个指针的内存空间。
调用基类中重名的方法
我们在Dancer和Singer中都声明了show方法。我们需要通过::作用域符号来指定哪一个show()方法才是我们想要的。
DancerSinger::show(){
Singer::show();
Dancer::show();
}
虚基类用于多重继承时,保留单个基类副本。虚函数用来将函数实行动态联编,使得可以基类指针或者引用调用派生类方法,从而实现多态性。虚基类和虚函数都是叫虚,只是因为使用了同一个关键字virtual,但是并没有什么联系。