• 【C++】继承 ⑫ ( 继承的二义性 | virtual 虚继承 )






    一、继承的二义性




    1、场景说明 - 继承的二义性


    A 类 是 父类 ,

    B 类 和 C 类 继承 A 类 , 是 子类 ,

    D 类 多继承 B 类 和 C 类 , 是 孙子类 ;


    在这里插入图片描述

    假如 A 类中有 成员变量 x ,

    则 子类 B 类 和 C 类 都会继承该 成员变量 x ,

    D 类 多继承 B 类 和 C 类 , 会 分别从 B 和 C 各自 继承一个 成员变量 x ;

    D 类中 , 从 B , C 两个父类中继承自 爷爷类 A 的成员变量 , 会出现二义性 ;


    代码如下 :

    #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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2、继承中的二义性报错


    如果强行使用 对象.x 访问继承自 A , 会报错 error C2385: 对“x”的访问不明确 ;

    定义 D 类的对象 d , 如果访问 继承自 A 类的 x 成员 , 则会出现二义性 ;

    	// 定义 D 类对象 d
    	D d;
    
    	// 访问 继承自 A 类的 x 成员出现二义性
    	// 报错 error C2385: 对“x”的访问不明确
    	d.x = 40;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    完整报错信息 :

    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==========
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、完整代码示例



    代码示例 :

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    执行结果 :

    在这里插入图片描述





    二、virtual 虚继承




    1、虚继承引入


    在多继承中 , 如果一个类继承了多个含有相同基类的派生类 , 就会产生菱形继承结构 ;

    这种情况下 , 可能会出现多个不同的基类实例 , 导致重复定义和二义性 ;

    为了应对上述 继承的二义性 问题 ,

    C++ 语言 使用 " 虚继承 " 解决 继承中的 二义性问题 ;


    C++ 中的 " 虚继承 " 是一种解决 多继承 带来的 菱形问题(diamond problem)的技术 ;

    虚继承的目的是 确保每个基类只被继承一次 , 从而避免 重复定义 和 二义性等问题 ;


    虚继承 通过在 派生类 中使用关键字 virtual 来指示基类应该被虚继承 , 虚继承确保了每个基类只被继承一次 , 从而避免了重复定义和二义性 ;

    在 C++ 中,使用虚继承的语法是在基类列表中使用 virtual 关键字 ;


    2、虚继承语法


    虚继承语法 : 在 继承的 访问限定符 之前 , 添加 virtual 关键字 , 将该继承行为定义为 " 虚继承 " ;

    class 子类类名 : virtual 访问限定符 父类类名
    {
    	// 子类内容
    }
    
    • 1
    • 2
    • 3
    • 4

    下面的 B 类 和 C 类 , 就是 虚继承 类 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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3、代码示例 - 虚继承


    代码示例 :

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    执行结果 :

    在这里插入图片描述

  • 相关阅读:
    C++类型转换
    基于SSM的人力资源管理系统,高质量毕业论文范例-可直接使用
    wireshark远程抓包
    使用 Aeraki Mesh 实现零代码侵入的 Dubbo 服务调用跟踪
    第三方支付在结算资金时的特殊处理方案
    第三章:高精度算法(加、减、乘、除)
    探索GitHub上的两个革命性开源项目
    Python数据分析11——Seaborn绘图
    nginx部署
    【OpenCV】-查找并绘制轮廓
  • 原文地址:https://blog.csdn.net/han1202012/article/details/134044415