• 【C++】继承 ⑬ ( 虚继承原理 | 虚继承解决继承二义性问题 | 二义性产生的原因分析 )






    一、虚继承原理




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

    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 类的构造函数只调用了一次 , 这样避免了 二义性产生 ;


    代码示例 :

    #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;
    }
    
    • 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
    • 50
    • 51
    • 52
    • 53
    • 54

    2、执行结果


    执行结果 : A 类的构造函数只调用了一次 , D 类中只有一个 A 类子对象 , 避免了二义性产生 ;

    A 构造函数
    B 构造函数
    C 构造函数
    D 构造函数
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

  • 相关阅读:
    RP2040 C SDK ADC功能使用
    RFID在钢筋仓库管理中的应用
    源码安装Openlava 4.0
    学习《Java核心技术》——第7章:异常、断言和日志
    服务器被DDOS与CC攻击了怎么办,要如何防御
    2023自动化测试面试题(含答案)
    Java【并发】面试题
    C++17结构化绑定
    在ensp中为什么有些网络的下一跳是127.0.0.1
    【ARM】MDK自动备份源文件
  • 原文地址:https://blog.csdn.net/han1202012/article/details/134049388