一、继承的二义性
1、场景说明 - 继承的二义性
A 类 是 父类 ,
B 类 和 C 类 继承 A 类 , 是 子类 ,
D 类 多继承 B 类 和 C 类 , 是 孙子类 ;
假如 A 类中有 成员变量 x ,
则 子类 B 类 和 C 类 都会继承该 成员变量 x ,
D 类 多继承 B 类 和 C 类 , 会 分别从 B 和 C 各自 继承一个 成员变量 x ;
D 类中 , 从 B , C 两个父类中继承自 爷爷类 A 的成员变量 , 会出现二义性 ;
代码如下 :
代码语言:javascript复制#include "iostream"
using namespace std;
class A {
public:
int x;
};
// 子类 B 继承了父类 A 的 x 成员
class B : public A {
public:
int y;
};
// 子类 C 继承了父类 A 的 x 成员
class C : public A {
public:
int z;
};
// D 多继承 B 和 C
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
int k;
};
2、继承中的二义性报错
如果强行使用 对象.x
访问继承自 A , 会报错 error C2385: 对“x”的访问不明确 ;
定义 D 类的对象 d , 如果访问 继承自 A 类的 x 成员 , 则会出现二义性 ;
代码语言:javascript复制 // 定义 D 类对象 d
D d;
// 访问 继承自 A 类的 x 成员出现二义性
// 报错 error C2385: 对“x”的访问不明确
d.x = 40;
完整报错信息 :
代码语言:javascript复制1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>Test.cpp(41,6): error C2385: 对“x”的访问不明确
1>Test.cpp(41,6): message : 可能是“x”(位于基“A”中)
1>Test.cpp(41,6): message : 也可能是“x”(位于基“A”中)
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
3、完整代码示例
代码示例 :
代码语言:javascript复制#include "iostream"
using namespace std;
class A {
public:
int x;
};
// 子类 B 继承了父类 A 的 x 成员
class B : public A {
public:
int y;
};
// 子类 C 继承了父类 A 的 x 成员
class C : public A {
public:
int z;
};
// D 多继承 B 和 C
// 分别从 B 和 C 各自继承一个来自 A 的成员 x
class D : public B, public C {
public:
int k;
};
int main() {
// 定义 D 类对象 d
D d;
// 访问继承自 B 类的 y 成员
d.y = 10;
// 访问继承自 C 类的 z 成员
d.z = 20;
// 访问 D 类自己的成员 k
d.k = 30;
// 访问 继承自 A 类的 x 成员出现二义性
// 报错 error C2385: 对“x”的访问不明确
//d.x = 40;
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :
二、virtual 虚继承
1、虚继承引入
在多继承中 , 如果一个类继承了多个含有相同基类的派生类 , 就会产生菱形继承结构 ;
这种情况下 , 可能会出现多个不同的基类实例 , 导致重复定义和二义性 ;
为了应对上述 继承的二义性 问题 ,
C 语言 使用 " 虚继承 " 解决 继承中的 二义性问题 ;
C 中的 " 虚继承 " 是一种解决 多继承 带来的 菱形问题(diamond problem)的技术 ;
虚继承的目的是 确保每个基类只被继承一次 , 从而避免 重复定义 和 二义性等问题 ;
虚继承 通过在 派生类 中使用关键字 virtual 来指示基类应该被虚继承 , 虚继承确保了每个基类只被继承一次 , 从而避免了重复定义和二义性 ;
在 C 中,使用虚继承的语法是在基类列表中使用 virtual 关键字 ;
2、虚继承语法
虚继承语法 : 在 继承的 访问限定符 之前 , 添加 virtual 关键字 , 将该继承行为定义为 " 虚继承 " ;
代码语言:javascript复制class 子类类名 : virtual 访问限定符 父类类名
{
// 子类内容
}
下面的 B 类 和 C 类 , 就是 虚继承 类 A ;
代码语言:javascript复制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;
};
3、代码示例 - 虚继承
代码示例 :
代码语言:javascript复制#include "iostream"
using namespace std;
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;
};
int main() {
// 定义 D 类对象 d
D d;
// 访问继承自 B 类的 y 成员
d.y = 10;
// 访问继承自 C 类的 z 成员
d.z = 20;
// 访问 D 类自己的成员 k
d.k = 30;
// 访问 继承自 A 类的 x 成员出现二义性
// 报错 error C2385: 对“x”的访问不明确
// 使用 virtual 虚继承后 , 不会报错
d.x = 40;
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :