多重继承中的二义性问题

多重继承:一个类同时继承了多个类,叫做多重继承。当派生类实例化的时候,会根据继承的顺序调用基类的构造函数,与构造函数初始化列表中基类构造函数调用的顺序无关

菱形继承(钻石继承):有一个基类A,同时被派生类B和C继承,类D同时继承B和C,那么D通过拥有两个A,当使用A里面的变量或者函数的时候,就会面临选择,这就是菱形问题。

菱形问题中涉及了多重继承 所以这个两个都会设计到二义性问题

那么什么是二义性问题呢?

二义性问题:

  1. 多重继承中的二义性
    • 当一个类继承自多个基类,而这些基类中含有同名的成员(如方法或变量)时,就会产生二义性。编译器无法确定应该使用哪个基类的成员。
    • 解决方法包括使用作用域解析运算符(如C++中的::)来明确指定要使用的基类成员,或者使用虚继承来避免多重继承带来的问题。
  2. 方法重写中的二义性
    • 在面向对象编程中,子类可以重写父类的方法。然而,如果子类中的方法签名与父类中的方法不完全相同(例如,由于参数类型不同导致的重载),但在某些情况下又表现出相似的行为,那么可能会引发二义性。
    • 解决方法包括确保方法签名的一致性,或者使用不同的方法名来避免混淆。
  3. 运算符重载中的二义性
    • 在支持运算符重载的语言中,同一个运算符可以被赋予多种含义。这可能导致表达式在不同的上下文中具有不同的解释,从而产生二义性。
    • 解决方法包括为运算符重载提供明确的语义规则,并在文档中清晰说明这些规则。

这里我们只讨论多重继承中的二义性问题

解决二义性问题本人给出两种方法:

1. 明确指定基类

2. 虚基类

明确指定基类:类 c 虽然有两个基类 在使用类c中的printx()会出现访问冲突的问题 这时候无法判断到底是访问基类a中的printx()还是基类b中的printx();就会报错 如果我们能明确指明是访问哪个基类中的printx 就能避免错误;以此解决多重继承中的二义性问题

报错内容:

修改后的内容:

//多重继承
#include <iostream>

using namespace std;

class a
{
protected:
    int x;
public:
    a()
    {
        cout<<"这是a的构造函数"<<endl;
        x = 0;
    }

    a(int x)
    {
        cout<<"这是a的带参数的构造函数"<<endl;
        this->x = x;
    }
    ~a()
    {
        cout<<"这是a的析构函数"<<endl;
    }

    void printx()
    {
        cout<<this->x<<endl;
    }

};


class b
{
protected:
    int x;
public:
    b()
    {
        cout<<"这是b的构造函数"<<endl;
        x = 0;
    }

    b(int x)
    {
        cout<<"这是b的带参数的构造函数"<<endl;
        this->x = x;
    }
    ~b()
    {
        cout<<"这是b的析构函数"<<endl;
    }
    void printx()
    {
        cout<<this->x<<endl;
    }


};

class c:public a,public b
{
public:
    c(int x):b(x),a(x)//显式调用
    {

        cout<<"这是c带参数的构造函数"<<endl;
    }

    ~c()
    {
        cout<<"这是c的析构函数"<<endl;
    }



};



int main()
{
 //二义性问题:解决
  //1.明确指明 基类
    c c1(399);
    c1.b::printx();
    return 0;
}

通过virtual关键字将class b class c定义为虚基类 使得类b 类a 两个类只有一份基类 a 的实例,
避免了访问冲突
//菱形继承
#include <iostream>

using namespace std;

class a
{
protected:
    int x;
public:
    a()
    {
        cout<<"这是a的构造函数"<<endl;
        x = 0;
    }

    a(int x)
    {
        cout<<"这是a的带参数的构造函数"<<endl;
        this->x = x;
    }
    ~a()
    {
        cout<<"这是a的析构函数"<<endl;
    }

    void printx()
    {
        cout<<this->x<<endl;
    }

};


class b :virtual public a
{
protected:
    int x;
public:
    b()
    {
        cout<<"这是b的构造函数"<<endl;
        x = 0;
    }

    b(int x)
    {
        cout<<"这是b的带参数的构造函数"<<endl;
        this->x = x;
    }
    ~b()
    {
        cout<<"这是b的析构函数"<<endl;
    }
    void printx()
    {
        cout<<this->x<<endl;
    }


};

class c:virtual public a
{
public:
    c(int x)
    {

        cout<<"这是c带参数的构造函数"<<endl;
    }

    ~c()
    {
        cout<<"这是c的析构函数"<<endl;
    }



};


class d:public c,public b
{
public:
    d(int x):b(x),c(x)//显式调用
    {

        cout<<"这是d带参数的构造函数"<<endl;
    }

    ~d()
    {
        cout<<"这是d的析构函数"<<endl;
    }



};



int main()
{

    d d1(188);
    d1.printx();
    return 0;
}

 这里使用了virtual关键字解决了菱形问题中的继承问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值