C++知识点53——虚继承

二、虚继承

1.概念

默认情况下,C++的派生列表中不允许同一个基类出现两次,但是,如果两个基类都继承了同一个类A,那么两个基类派生出的子类就会包含两次类A的部分

为了解决上述问题,C++中就出现了虚继承,通过虚继承,无论虚基类在整个继承链中出现了多少次,子类中都只出现一次。

示例

class animal
{
public:
	animal(){cout<<__func__<<endl;}
	~animal(){cout<<__func__<<endl;}
};

class bear:public virtual animal
{
public:
	bear(){cout<<__func__<<endl;}
	~bear(){cout<<__func__<<endl;}
};

class cat:public virtual animal
{
public:
	cat(){cout<<__func__<<endl;}
	~cat(){cout<<__func__<<endl;}
};

class endangered
{
public:
	endangered(){cout<<__func__<<endl;}
	~endangered(){cout<<__func__<<endl;}
};

class panda:public cat, public bear, public endangered
{
public:
	panda(){cout<<__func__<<endl;}
	~panda(){cout<<__func__<<endl;}
};

int main(int argc, char const *argv[])
{
	panda t;
	return 0;
}

上述代码就是个虚继承的例子,输出结果如下

通过输出结果显示,animal只创建了一次,如果将派生列表的virtual关键字去掉,输出结果如下

所以,上述代码通过虚继承,让基类animal只在子类中出现了一次

 

2.规则

和单一继承与多重继承一样,虚继承的的子类对象可以切割转化成对应的基类,可以通过基类的指针或者引用指向子类对象

成员的查找规则和单一继承以及多重继承一样

见博客https://blog.csdn.net/Master_Cui/article/details/110119371 https://blog.csdn.net/Master_Cui/article/details/109899018 https://blog.csdn.net/Master_Cui/article/details/109849186

3.虚继承的问题

与非虚多重继承一样,如果在多个不同的基类中定义了同名的成员,会出现二义性问题,解决办法依然是指定作用于或者在子类中也定义同名的成员,见博客https://blog.csdn.net/Master_Cui/article/details/110119371

4.构造函数与虚继承

在单一继承和非虚多重继承的情况下,创建一个子类对象时,各个类的构造的顺序是按照派生列表中类出现的顺序来调用的。

但是虚继承不一样,在虚继承的体系中,创建一个子类对象时,虚基类总是先于非虚基类构造。

示例

class a
{
public:
	a(){cout<<__func__<<endl;}
	~a(){cout<<__func__<<endl;}
};

class b :virtual public a
{
public:
	b(){cout<<__func__<<endl;}
	~b(){cout<<__func__<<endl;}
};

class c
{
public:
	c(){cout<<__func__<<endl;}
	~c(){cout<<__func__<<endl;}
};

class d
{
public:
	d(){cout<<__func__<<endl;}
	~d(){cout<<__func__<<endl;}
};

class e:public c, public b, virtual public d  
{
public:
	e(){cout<<__func__<<endl;}
	~e(){cout<<__func__<<endl;}
};

当一个e的对象被创建时,输出结果是这样的

过程如下:首先查看派生列表。发现有虚基类,第一个发现的虚基类时a,所以先创建a部分,调用a的构造函数,然后发现了虚基类d,于是就创建d并调用d的构造函数,虚基类都创建完成了,开始创建非虚基类,非虚基类在派生列表中的出现顺序分别是c,b,所以创建并调用了c和b的构造函数,最后创建e并调用e的构造函数

析构顺序和构造的顺序正好相反

 

如果一个虚基类既是基类也是子类,在创建虚基类的时候,依然会创建虚基类的基类

示例

class a
{
public:
	a(){cout<<__func__<<endl;}
	~a(){cout<<__func__<<endl;}
};

class b : public a
{
public:
	b(){cout<<__func__<<endl;}
	~b(){cout<<__func__<<endl;}
};

class c:public virtual b
{
public:
	c(){cout<<__func__<<endl;}
	~c(){cout<<__func__<<endl;}
};

class d:public virtual b
{
public:
	d(){cout<<__func__<<endl;}
	~d(){cout<<__func__<<endl;}
};

class e:public c, public d  
{
public:
	e(){cout<<__func__<<endl;}
	~e(){cout<<__func__<<endl;}
};

class f:public e, public a
{
public:
	f(){cout<<__func__<<endl;}
	~f(){cout<<__func__<<endl;}
};

代码的输出结果如下

上述代码中,先出现了一条警告,这是因为a在f的对象中存在两份,当访问a中的元素时,无法确定到时是访问哪个a中的元素,编译器分不出来,但是只要不访问a中的元素编译器就不会报错

因为在f的派生类列表中先发现的是虚基类b,而虚基类b又是a的子类,所以先创建a,然后创建b,之后按照非虚基类在派生列表中出现的顺序分别创建cde,接着在派生列表中又发现了一个a,于是又创建了一个a,最后创建f,析构函数的调用顺序仍然和构造函数相反

 

所以,当一个基类在子类对象中创建两份时,访问出现两份的基类成员时会出现二义性问题,因为编译器无法区分到底要访问哪个基类中的对应的成员

比如在a中添加一个成员at,访问时,就会出现如下错误

提示有二义性

多重继承也有这个问题

 

所以在实际应用中,虚继承和多重继承用的很少,单一继承用的较多

 

参考

《C++ Primer》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值