上篇C++57个入门知识点_49 多重继承与组合(一个类同时具有多个类的属性的方法:多重继承或者组合;多重继承:一个类同时继承多个类;多重继承构造和析构的顺序与普通继承类似;组合:类中包含多个成员对象)介绍了多重继承及组合,实际项目中使用多重继承的情况并不多见,因为他会出现一种特殊的情况,称之为菱形继承。
1. 菱形继承的定义及使用
在C++中如果
A
是
B
、
C
的父类,而
D
又多重继承于
B
、
C
组成的继承关系很像一个菱形,我们把这种继承关系称之为
菱形继承。
这种继承关系,天生的语法缺陷,会导致D的对象在调用A中的成员时,存在调用不明确的问题。
这就像:生物上三代以内不能结婚,否则父母致病的隐形基因可能显示在子代中,菱形继承就是一种近亲结婚现象
2. 菱形继承的问题
根据菱形继承的定义,以下代码中定义
CFurniture
类作为
CSofa
类和
CBed
类的父类,
CSofaBed
继承自
CSofa
类和
CBed
类,此时用户通过
CSofaBed
对象调用
CFurniture
类的成员变量,会发生什么问题呢?
#include <iostream>
//菱形继承
//家具类
class CFurniture
{
public:
CFurniture() {
printf("CFurniture::CFurniture()\r\n");
m_nFurniture = 0;
}
~CFurniture() {
printf("~CFurniture()\r\n");
}
void use() {
printf("sit\r\n");
}
public:
int m_nFurniture;
};
//虚继承
class CSofa :public CFurniture
{
public:
CSofa() {
m_nSofa = 1;
printf("CSofa()\r\n");
}
~CSofa() {
printf("~CSofa()\r\n");
}
void Sit() {
printf("sit\r\n");
}
public:
int m_nSofa;
};
class CBed :public CFurniture
{
public:
CBed() {
m_nBed = 2;
printf("CBed()\r\n");
}
~CBed() {
printf("~CBed()\r\n");
}
void Sleep() {
printf("Sleep\r\n");
}
public:
int m_nBed;
};
//某一个类既有沙发又有床的特点
//多重继承
class CSofaBed :public CSofa, public CBed
{
public:
CSofaBed() {
printf("CSofaBed()\r\n");
}
~CSofaBed() {
printf("~CSofaBed()\r\n");
}
int m_nSofaBed = 3;
};
int main(int argc, char* argv[])
{
//创建沙发床对象
CSofaBed sofaBed;
sofaBed.m_nFurniture = 1;
return 0;
}
编译结果如下:显示对CFurniture
类的成员变量的访问不明确
报错原因需要查看sofaBed
内存结构,其父类CSofa
类和CBed
类中都有CFurniture
类的成员变量m_nFurniture
,也就造成编译器在编译sofaBed.m_nFurniture = 1;
时不明确针对的是哪一个父类中变量。
上面提到的模型即为:D
的对象在调用A
中的成员,会发现,B、C
中分别都有一个A的成员,造成一个D
对象,2个A
成员的对应关系。
- 当然我们可以通过指定作用域的方式来明确父类中变量
以下代码中通过指定sofaBed
中调用的是CSofa
或CBed
某一类域的方式,来明确调用哪个父类内容
int main(int argc, char* argv[])
{
//创建沙发床对象
CSofaBed sofaBed;
sofaBed.CSofa::m_nFurniture = 1;
sofaBed.CBed::m_nFurniture = 1;
return 0;
}
除了成员变量,成员函数的调用也是类似的
int main(int argc,char* argv[])
{
//创建沙发床对象
CSofaBed sofaBed;
//菱形继承,CFurniture类中的成员变量继承到了两个子类中,需要加上作用域才能指明具体位置
sofaBed.CSofa::m_nFurniture = 1;
sofaBed.CBed::m_nFurniture = 1;
sofaBed.use();//存在this指针调用不明确的问题
//成员函数与成员变量一样的方式
sofaBed.CSofa::use();
sofaBed.CBed::use();
return 0;
}
问题虽然解决了,按道理sofaBed
只是一套家具,应该只有一份m_nFurniture
变量,但是上面代码实现,不管如何去写都是具有2份变量。正是由于2份变量的存在,才会造成语法上的不明确,这是语法上菱形继承天生的缺陷。虚继承的出现就是为了打破这种近亲结婚的现象!
3.菱形继承的解决方法:虚继承,及建议
生物上三代以内不能结婚,否则父母致病的隐形基因可能显示在子代中,菱形继承就是一种近亲结婚现象。上面代码中
sofaBed
的父类
CSofa
和
CBed
都继承自
CFurniture
,都继承了
m_nFurniture
的致病因子,因此在后代中就会造成冲突。
通过CSofa
和CBed
增加virtual
关键字实现虚继承,就可以解决近亲结婚的问题,其写法如下:
class CSofa :virtual public CFurniture{......}
class CBed :virtual public CFurniture{......}
采用虚继承之后,不管是通过B、C
指向的A
的成员,都是一个地址下的成员变量,即sofaBed
的父类中的m_nFurniture
是公有的,因此通过让子类只有一份隐形基因的方式解决了近亲结婚生病的问题。
同样的对成员函数的效果也是一样的
这是C++中的允许使用多重继承造成的菱形继承的缺陷,这也是实际项目中比较少用菱形继承的原因。
4.学习视频地址:C++57个入门知识点_50 菱形继承与虚继承