虚基类和多继承

我们声明一个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,但是并没有什么联系。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值