C++ 二义性是什么?怎么解决?

一、什么是二义性

在多继承的场景里,当父类中存在同名变量时,子类访问父类的同名变量,将出现二义性,因为编译器不知道你将要访问的是哪个父类中的变量。

举个例子:

class A
{
public:
    int a; // B1,B2 都将继承一个变量 a
};
class B1 : public A
{
};
class B2 : public A
{
};
class C : public B1, public B2
{
};

int main()
{
    C c;
    c.a = 10; // ERROR ! 二义性 !!!
    return 0;
}

 

二、怎么解决

1. 不使用多继承

一般来说,单继承就可以满足我们 99% 的需求了,我们应该尽量避免使用多继承带来的二义性问题。(注意:这里说的单继承不包括下面说的这种类似于 “接口” 的父类)。

由于 C++ 中不存在接口,但是可以使用只包含纯虚函数的抽象类替代,如果是只包含纯虚函数的抽象类,再多继承都将不会发生二义性(父类都没有变量了当然不会有二义性)。

2. 使用虚继承

虚继承只能解决多个父类的同名变量都是从公共基类中继承而来的情况,就是下图这种:

使用虚继承:

class A
{
public:
    int a;
};
class B1 : virtual public A // 虚继承
{
};
class B2 : virtual public A // 虚继承
{
};
class C : public B1, public B2
{
};
 
int main()
{
    C c;
    c.a = 10; // OK,不会有二义性了
    return 0;
}

原理:使用虚继承时,C++ 编译器会做特殊处理,只会调用一个公共基类 A 的构造方法,这样就不会创建出多个同名变量 a 了。

3. 使用 “类名::变量名” 显性访问

还有一种二义性出现的场景,就是多个父类都是独立的,它们没有公共基类,这些独立的父类中存在同名变量的话,就不能使用虚继承来解决了,类似下图这样子:

这种情况,我们就只能使用 “类名::变量名” 显性访问,避免二义性了:

class B1
{
public:
    int a;
};
class B2
{
public:
    int a;
};
class C : public B1, public B2
{
};

int main()
{
    C c;
    c.B1::a = 10;
    c.B2::a = 20;
    return 0;
}

同样,如果父类还存在同名方法,我们也可以使用 “类名::方法名” 这样显性调用。

C++中,当派生类通过多重继承继承了具有相同名称的成员函数或数据成员的两个或更多基类时,编译器会遇到二义性问题。为了解决这种二义性,可以采用以下方法: 参考资源链接:[C++多重继承与二义性解决方案详解](https://wenku.csdn.net/doc/7wmmgabbgo?spm=1055.2569.3001.10343) 1. 成员名限定:通过使用作用域解析操作符`::`来明确指定调用哪个基类的成员。例如,如果有两个基类`Base1`和`Base2`都有成员函数`func()`,在派生类中可以这样调用`Base1::func()`来指定调用`Base1`中的函数。 2. 虚拟继承:使用虚拟继承来避免公共基类在派生类中产生多个实例,从而解决二义性问题。虚拟继承确保只存在一个基类的实例,无论派生多少层。 3. 函数隐藏:在派生类中重新定义同名的成员函数,这样就可以根据派生类的需要来覆盖基类的成员函数,从而避免二义性。 4. 虚函数:通过在基类中声明成员函数为虚函数(使用`virtual`关键字),允许派生类重写这些函数,实现动态绑定。这有助于在运行时解决二义性问题,因为调用的是对象的实际类型对应的成员函数。 5. 上行转换与下行转换:在向上转换(子类指针转父类指针)时,如果存在公共基类,可以通过显式转换来指定转换的路径,从而消除二义性。在向下转换(父类指针转子类指针)时,使用`dynamic_cast`可以安全地转换指针或引用,并在不安全的情况下返回空指针或抛出异常。 了解和掌握这些方法能够有效地解决多重继承带来的二义性问题,并编写出更加清晰和健壮的C++代码。为了深入理解这些概念并看到更多的实际应用示例,强烈推荐阅读《C++多重继承与二义性解决方案详解》。这本书详细讲解了多重继承中的二义性问题及其解决方案,并提供了丰富的代码示例,帮助读者更好地理解和应用这些知识。 参考资源链接:[C++多重继承与二义性解决方案详解](https://wenku.csdn.net/doc/7wmmgabbgo?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值