一、虚继承原理
1、虚继承解决继承二义性问题
继承的二义性 : 如果 一个 子类 ( 派生类 ) 继承多个 父类 ( 基类 ) , 这些父类 都继承了 相同的父类 , 那么 子类 访问 父类的父类 中的成员 , 就会产生 二义性 ;
- 报错 : error C2385: 对“x”的访问不明确 ;
使用 " 虚继承 " 可以解决上述问题 , 子类 继承父类时 , 在 访问限定符 之前使用 virtual 关键字 , 即可将 普通继承 改为 虚继承 ;
- 下面的代码中 A 是父类 ;
- B 类 和 C 类 虚继承 A 类 , 这样当 某个类 同时 多继承 B 类 和 C 类时 , 访问 A 类中的成员时 , 不会出现 二义性 ;
- 由于 B 和 C 虚继承 A , D 类访问 A 中的成员 , 不会产生二义性 ;
class A {
public:
int x;
};
// 子类 B 继承了父类 A 的 x 成员
class B : virtual public A {
public:
int y;
};
// 子类 C 继承了父类 A 的 x 成员
class C : virtual public A {
public:
int z;
};
// D 多继承 B 和 C
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
int k;
};
2、二义性产生的原因分析
二义性产生的原因 :
- 如果 上述 B 和 C 类 没有 虚继承 A 类 ;
- 上述 D 对象 创建时 , 会调用 两次 A 的构造函数 , 一次由 B 对象调用 , 一次由 C 对象调用 ;
- 此时 D 对象中就包含了 两个 A 类的 子对象 ;
- 当 访问 A 类的成员时 , 不知道访问哪个 A 类的成员 , 就出现了二义性 ;
3、虚继承原理
使用 虚继承 后 , 在调用 虚继承 父类 构造函数时 , 只调用一次 ;
构建 D 类对象的 流程 如下 :
- 先构建 B 类对象 , 调用了一次 A 的构造函数 , 构造了 A 类子对象 ;
- 再构建 C 类对象 , 发现已经调用了一次 A 的构造函数 , 不会再次构造 第二个 A 类子对象 ;
只有一个 A 类子对象 , 这样就避免了 二义性 的产生 ;
二、代码示例 - 虚继承原理
1、完整代码示例
在下面的代码中 ,
为 A 类 , B 类 , C 类 , D 类 , 都定义一个默认的 无参构造函数 ,
每个构造函数 中打印相关信息 ;
B 类 和 C 类都 虚继承 A 类 ,
最终构建 D 类使 , 发现 A 类的构造函数只调用了一次 , 这样避免了 二义性产生 ;
代码示例 :
代码语言:javascript复制#include "iostream"
using namespace std;
class A {
public:
int x;
A()
{
cout << "A 构造函数" << endl;
}
};
// 子类 B 继承了父类 A 的 x 成员
class B : virtual public A {
public:
int y;
B()
{
cout << "B 构造函数" << endl;
}
};
// 子类 C 继承了父类 A 的 x 成员
class C : virtual public A {
public:
int z;
C()
{
cout << "C 构造函数" << endl;
}
};
// D 多继承 B 和 C
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
int k;
D()
{
cout << "D 构造函数" << endl;
}
};
int main() {
D d;
d.x = 10;
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
2、执行结果
执行结果 : A 类的构造函数只调用了一次 , D 类中只有一个 A 类子对象 , 避免了二义性产生 ;
代码语言:javascript复制A 构造函数
B 构造函数
C 构造函数
D 构造函数
Press any key to continue . . .