• 【C++入门】多继承(菱形继承)及其二义性问题


    1、二义性问题

    1.1、示例代码

    #include 
    #include 
    
    using namespace std;
    
    class A
    {
    public:
    	void speak(void);
    };
    
    class B
    {
    public:
    	void speak(void);
    
    };
    
    class C:public A, public B
    {
    public:
    
    };
    
    
    
    int main(void)
    {
    	C variable;
    
    	variable.speak();	//使用speak成员时存在二义性问题,编译报错
    
    	//variable.A::speak();  //明确指定,没有歧义
    	//variable.B::speak();	//明确指定,没有歧义
    	
    	return 0;
    } 
    
    void A::speak()
    {
    	cout << "A::speak" << endl;
    }
    
    void B::speak()
    {
    	cout << "B::speak" << endl;
    }
    
    • 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

    1.2、代码分析

    root@ubuntu:# g++ test.cpp -o app --std=c++11
    test.cpp: In function ‘int main()’:
    test.cpp:31:11: error: request for member ‘speak’ is ambiguous
      variable.speak();
    test.cpp:15:7: note: candidates are: void B::speak()
      void speak(void);
    test.cpp:9:7: note:                 void A::speak()
      void speak(void);
    make: *** [all] Error 1
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (1)在A和B类中存在同名的speak成员;
    (2)C同时继承A和B两个类,此时C中存在两个不同命名空间但是同名的成员(speak),当C类的对象使用speak成员时,编译器不知道该使用A类的speak,还是B类的speak;

    2、解决二义性的方法

    (1)解决办法1:避免出现,让A和B的public成员命名不要重复冲突。但这个有时不可控。
    (2)解决办法2:编码时明确指定要调用哪一个,用variable.A::speak()明确指定调用的是class A的speak而不是class B的
    (3)解决办法3:在C中重定义speak,则调用时会调用C中的speak,A和B中的都被隐藏了;
    补充:对C++的隐藏特性不清楚的可以查看博客:《【C++入门】访问权限管控和继承机制详解》

    3、菱形继承问题

    3.1、产生菱形继承问题的原因

    在这里插入图片描述

    (1)产生菱形继承的原因如上图,C继承A和B,但是A和B都继承Base类,于是A和B都各自有一份Base类的成员;
    (2)当C继承A和B时,此时C中就有两份Base类的成员,于是就产生了歧义;如果C类中使用Base类的成员,就不知道使用A中的那份还是B中的那份;

    3.2、示例代码

    #include 
    #include 
    
    using namespace std;
    
    
    class Base
    {
    public:
    	int dataBase;
    
    };
    
    class A: public Base
    {
    public:
    	int dataA;
    
    };
    
    
    class B: public Base
    {
    public:
    	int dataB;
    };
    
    class C:public A,public B
    {
    public:
    	int dataC;
    
    };
    
    int main(void)
    {
    	C param;
    
    	param.dataBase;	//产生歧义
    
    	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

    3.3、示例代码分析

    [root@]$ g++ main.cpp -o app --std=c++11
    main.cpp: In function ‘int main()’:
    main.cpp:39:8: error: request for member ‘dataBase’ is ambiguous
      param.dataBase;
            ^
    main.cpp:10:6: note: candidates are: int Base::dataBase
      int dataBase;
          ^
    main.cpp:10:6: note:                 int Base::dataBase
    make: *** [all] Error 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    C类对象在使用dataBase成员时,不知道使用A类中的dataBase,还是B中的dataBase,产生歧义,于是编译报错;

    3.4、虚继承解决菱形继承问题

    3.4.1、示例代码

    #include 
    #include 
    
    using namespace std;
    
    
    class Base
    {
    public:
    	int dataBase;
    
    };
    
    class A: virtual public Base	//virtual:表示虚继承
    {
    public:
    	int dataA;
    
    };
    
    
    class B: virtual public Base	//virtual:表示虚继承
    {
    public:
    	int dataB;
    };
    
    class C:public A,public B
    {
    public:
    	int dataC;
    
    };
    
    int main(void)
    {
    	C param;
    
    	param.dataBase = 1;
    
    	cout << "param.dataBase=" << param.dataBase << endl;
    
    	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

    使用virtual关键字,让A和B都虚继承Base类;

    3.4.2、虚继承实现的原理

    实现原理参考博客:《【C++入门】虚继承的实现原理》

  • 相关阅读:
    QoS小结
    中文关键字提取-TextRank
    2023年总结以及对2024年的展望
    电商转化率这么抽象,到底是个啥?
    QScrollBar滚动条、QSlider滑块、 QDial表盘
    Stream流
    仿钉钉考勤统计页面的日历组件,通过日历展示每日考勤打卡情况,支持在日历上打两种不同类型的点,大致适配各种分辨率效果图
    /dev下没有video0这个文件(ubuntu无法打开摄像头)
    狗都能看懂的CenterNet讲解及代码复现
    C++ 统计程序运行时间
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/127345314