C++多继承中二义性的解决方案

article/2025/10/14 20:36:40

        出现二义性的原因: 派生类在访问基类成员函数时,由于基类存在同名的成员函数,导致无法确定访问的是哪个基类的成员函数,因此出现了二义性错误。

1. 什么是多重继承的二义性

class A{
public:void f();
}class B{
public:void f();void g();
}class C:public A,public B{
public:void g();void h();
};

 

 如果声明:C c1,则c1.f();具有二义性,而c1.g();无二义性(同名覆盖)。

 

2. 解决办法一 -- 类名限定

调用时指名调用的是哪个类的函数,如

c1.A::f();
c1.B::f();

 

3. 解决办法二 -- 同名覆盖

 

在C中声明一个同名函数,该函数根据需要内部调用A的f或者是B的f。如

class C:public A,public B{
public:void g();void h();void f(){A::f();}
};


4. 解决办法三 -- 虚基类(用于有共同基类的场合)

 

virtual 修饰说明基类,如:

 

class B1:virtual public B

 

虚基类主要用来解决多继承时,可能对同一基类继承继承多次从而产生的二义性。为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝。注意:需要在第一次继承时就要将共同的基类设计为虚基类。虚基类及其派生类构造函数建立对象时所指定的类称为最(远)派生类。

 

  • 虚基类的成员是由派生类的构造函数通过调用虚基类的构造函数进行初始化的。
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中给出对虚基类的构造函数的调用。如果未列出,则表示调用该虚基类的缺省构造函数。
  • 在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,该派生类的其他基类对虚基类的构造函数的调用被忽略。
    class B{public:int b;
    }class B1:virtual public B{priavte:int b1;
    }class B2:virutual public B{private:int b2;
    }class C:public B1,public B2{private:float d;
    }C obj;
    obj.b;//正确的

如果B1和B2不采用虚基类,则编译出错,提示“request for member 'b' is ambiguous”。这是因为,不指名virtual的继承,子类将父类的成员都复制到自己的空间中,所以,C中会有两个b。

下面我们来看两个例子:

#include<iostream>
using namespace std;class B0{
public:B0(int n)    {nv=n;cout<<"i am B0,my num is"<<nv<<endl;}void fun()    {cout<<"Member of Bo"<<endl;}
private:int nv;
};class B1:virtual public B0{
public:B1(int x,int y):B0(y){nv1=x;cout<<"i am  B1,my num is "<<nv1<<endl;}
private:int nv1;
};class B2:virtual public B0{
public:B2(int x,int y):B0(y){nv2=x;cout<<"i am B2,my num is "<<nv2<<endl;}
private:int nv2;
};class D:public B1,public B2{
public:D(int x,int y,int z,int k):B0(x),B1(y,y),B2(z,y){nvd=k;cout<<"i am D,my num is "<<nvd<<endl;}
private:int nvd;
};int main(){D d(1,2,3,4);d.fun();return 0;
}

结果:

i am B0,my num is 1
i am B1,my num is 2
i am B2,my num is 3
i am D,my num is 4
Member of Bo
/*多继承的二义性*/ 
#include <iostream>
using namespace std;class Base1{  //定义基类Base1 public:int var;void fun(){cout << "Member of Base1" << endl;}
}; 
class Base2{public:int var;void fun(){cout << "Member of Base2 " << endl;}
};
class Derived: public Base1, public Base2{  //定义派生类Derivedpublic:int var;   //同名数据成员void fun(){                         //同名函数成员 cout << "Member of Derived" << endl;  } 
};
int main(){Derived d;Derived *p = &d;d.var = 1;  //访问Derived类成员d.fun();d.Base1::var = 2;  //作用域分别标识符,避免二义性,访问基类中同名的数据成员d.Base1::fun();p->Base2::var = 3;  //作用域分别标识符,避免二义性,访问基类中同名的数据成员p->Base2::fun(); return 0;
}

 

 

下面我们再看一个使用虚基类解决二义性的问题的例子:

/*虚基类*/ 
#include <iostream>
using namespace std;class Base0{  //定义基类Base0public:int var0;void fun0(){cout << "Member of Base0" << endl;} 
};
class Base1: virtual public Base0{  //定义派生类Base1,第一级继承时就要将共同的基类设计为虚基类 public:int var1;void fun(){cout << "Member of Base1" << endl;}
}; 
class Base2: virtual public Base0{  //定义派生类Base2,第一级继承时就要将共同的基类设计为虚基类  public:int var2;void fun(){cout << "Member of Base2 " << endl;}
};
class Derived: public Base1, public Base2{//定义派生类Derived,因为Derived多继承而来的Base1,Base2,他们同时共同继承Base0, public:                               //所以空间里会产生两个var0和fun0(),对象调用他们时,会有二义性,但如果在他们继承Base0时使用关键字virtual就可以消除影响 int var;   //同名数据成员void fun(){                         //同名函数成员 cout << "Member of Derived" << endl;  } 
};
int main(){Derived d;d.var0 = 2;//直接访问虚基类的数据成员 d.fun0();return 0;
}

/*虚基类及其派生类的构造函数*/ 
#include <iostream>
using namespace std;class Base0{  //定义基类Base0public:Base0(int var): var0(var){} //构造函数 int var0;void fun0(){cout << "Member of Base0" << endl;} 
};
class Base1: virtual public Base0{  //定义派生类Base1,第一级继承时就要将共同的基类设计为虚基类 public:Base1(int var): Base0(var){}//派生类的构造函数向基类构造函数传参 int var1;void fun(){cout << "Member of Base1" << endl;}
}; 
class Base2: virtual public Base0{  //定义派生类Base2,第一级继承时就要将共同的基类设计为虚基类  public:Base2(int var): Base0(var){}int var2;void fun(){cout << "Member of Base2 " << endl;}
};
class Derived: public Base1, public Base2{//定义派生类Derived,因为Derived多继承而来的Base1,Base2,他们同时共同继承Base0, public: //所以空间里会产生两个var0和fun0(),对象调用他们时,会有二义性,但如果在他们继承Base0时使用关键字virtual就可以消除影响 Derived(int var): Base0(var), Base1(var), Base2(var){}int var;   //同名数据成员void fun(){ //同名函数成员 cout << "Member of Derived" << endl;  } 
};
int main(){Derived d(1);//Derived是最远派生类,它的构造函数会调用虚基类的构造函数,而Base1和Base2对虚基类的构造函数的调用都会被自动忽略 ,并且Base1和Base2也不会被初始化,只有Base0(1)初始化为1; d.var = 2;//直接访问虚基类的数据成员 cout << d.var0 << endl << d.var1 << endl << d.var2 << endl; d.fun();return 0;
}

由运行结果可以看的出最远派生类Derived的构造函数只对虚基类Base0进行了初始化,但Base1和Base2并没有初始化,依旧存放的垃圾数据。


http://chatgpt.dhexx.cn/article/E4VwZvml.shtml

相关文章

编译原理——证明文法的二义性(1)

目录 推导和语法树推导语法树 文法二义性 在证明文法的二义性之前&#xff0c;我们需要熟悉几个基本的概念。 推导和语法树 推导 这里的推导&#xff0c;简单的来说就是指根据给出的句型&#xff08;句子&#xff09;&#xff0c;对文法进行推理变化最终得到句型&#xff08…

二义性

自然语言的二义性什么意思 面这个问题.很清楚的说明了自然语言的二义性.. 用红墨水写一个“蓝”字&#xff0c;请问&#xff0c;这个字是红字还是蓝字&#xff1f; 可能很少有人意识到&#xff0c;像红字和蓝字这样的词语都存在着二义性。可能是红色的字&#xff0c;也可能是“…

2.5.2 文法的二义性

2.5.2 文法的二义性 设有文法 G [ E ]: E → E E | E * E | ( E ) | i句子 i * i i 有两个不同的最左推导,对应两棵不同的语法树,见图 2.6 和图 2.7 。 最左推导 1 E ⇒ E E ⇒ E * E E ⇒ i * E E⇒ i * i E⇒ i * i i 最左推导 2 E ⇒ E * E ⇒ i * E⇒ i * E E⇒…

编译原理(三)语法分析:3.二义性与二义性的消除

文章目录 一、二义性1.定义2.原因 二、二义性的消除1.改写二义文法为非二义文法&#xff08;1&#xff09;步骤&#xff08;2&#xff09;例子&#xff08;3&#xff09;缺点 2.为文法符号规定优先级和结合性3.修改语言的语法&#xff08;表现形式被改变&#xff09; 【编译原理…

二义性和C++消除二义性

1.二义性 二义性的定义是&#xff1a;“如果文法G中的某个句子存在不只一棵语法树&#xff0c;则称该句子是二义性的。如果文法含有二义性的句子&#xff0c;则称该文法是二义性的。”&#xff08;该定义来自于百度百科&#xff09;用通俗的话讲&#xff0c;如果一句话或者一个…

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

一、什么是二义性 在多继承的场景里&#xff0c;当父类中存在同名变量时&#xff0c;子类访问父类的同名变量&#xff0c;将出现二义性&#xff0c;因为编译器不知道你将要访问的是哪个父类中的变量。 举个例子&#xff1a; class A { public:int a; // B1&#xff0c;B2 都…

ES6一维数组去重,合并去重方法分享

ES6去重的几个小方法 1.使用set方法 2.setArray.from() 3.filtermap 4、Array.fromsetflatsort 排序去重 flat:对数组进行扁平化处理 sort:排序 b-a:倒序 a-b:正序 5.数组合并去重

[记录]es6常用去重方法(数组、字符串)

数组去重 ES6 ES6以下方法除了代码简洁外,对于undefined和NaN也同样可以达到去重的效果 new Set()是ES6新增的数据结构,类似于数组,但它的一大特性就是所有元素都是唯一的,没有重复的值,我们一般称为集合,Set本身是一个构造函数,用来生成 Set 数据结构。 Set搭配扩展运算符 ……

JavaScript数组去重—ES6的两种方式

说明 JavaScript数组去重这个问题&#xff0c;经常出现在面试题中&#xff0c;以前也写过一篇数组去重的文章&#xff0c;&#xff08;JavaScript 数组去重的多种方法原理详解&#xff09;但感觉代码还是有点不够简单&#xff0c;今天和大家再说两种方法&#xff0c;代码可是足…

ES6 Set() 数组去重

ES6 Set()去重 Set。它类似于数组&#xff0c;但是成员的值都是唯一的 通过add()方法向 Set 结构加入成员 let arr [1,2,3,4,1,5,2,3]; var set2 new Set(); arr.forEach(item>{set2.add(item) }) console.log(set2); console.log(Array.from(set2));let str [测试1,测试…

ES6数组去重的三个简单办法

ES6数组去重的三个简单办法 简单说一下利用ES6实现数组去重的三个办法。 第一种: 利用Map对象和数组的filter方法 贴上相关代码 打印后的结果 通过打印我们发现&#xff0c;确实实现了我们想要的效果。那么下面简单来解释一下。 1.Map对象是ES6提供的一个新的数据结构&…

一文弄懂Python中的*args 和 **kwargs

1. 引言 在本文中&#xff0c;我们将讨论 Python 中的 *args 和 **kwargs 及其用法和示例。 闲话少说&#xff0c;我们直接开始吧。 2. 问题引入 在Python中写函数的时候&#xff0c;我们经常需要给函数传值&#xff0c;这些值被称为函数参数。 我们不妨来举个栗子&#xff…

**kwargs python_python中**kwargs怎么用?

1、使用两个星号是收集关键字参数&#xff0c;可以将参数收集到一个字典中&#xff0c;参数的名字是字典的 “键”&#xff0c;对应的参数的值是字典的 “值”。请看下面的例子&#xff1a;>>> def print_kwargs(**kwargs): ... print(kwargs) ... >>> pr…

Python技巧:​args 和 kwargs 原来这么强大

大家好&#xff0c;今天我给大家分享Python技巧&#xff1a;​args 和 kwargs的相关技巧。喜欢记得收藏、关注、点赞。 注&#xff1a;完整代码、资料、技术交流文末获取 现在args和 kwargs参数仍然是 Python 中非常有用的特性&#xff0c;而且理解它们的威力将使您成为更有效…

*args和**kwargs是什么意思

去面试的时候&#xff0c;做了一道笔试题——什么是*args和**kwargs&#xff0c;区别在哪里&#xff1f; 有点蒙&#xff0c;好像见过&#xff0c;但是不知道具体的意思。所以回来查了一下资料&#xff0c;做一下笔记。 总的来说&#xff0c;*args代表任何多个无名参数&#x…

埃及分数c语言设计,埃及分数(四)

最优分解 源程序 下面就是求埃及分数最优分解的 C 语言源程序 EgyptianFraction.c&#xff1a; 1: #include 2: #include 3: #include 4: 5: const int SIZE 64; 6: static int wp -1, bp -1; 7: static mpz_t z, w, work[SIZE], best[SIZE]; 8: static mpq_t q1, q2; 9:…

埃及分数问题

问题描述: 古埃及人喜欢用最少的分子为 1 的真分数来表示一个真分数&#xff0c;比如7 / 8 1 / 2 1 / 3 1 / 24 。设计程序把一个真分数表示为最少的埃及分数之和的形式。 思路&#xff1a; 首先要知道什么是真分数&#xff1a;真分数是指大于0小于1的所有分数。这些分数的特…

贪心算法之埃及分数问题

1.问题&#xff1a;给定一个分数&#xff0c;如7/8&#xff0c;我们可以把它表示为1/2 1/3 1/24&#xff0c;埃及分数问题即把一个真分数表示为最少的埃及分数之和的形式&#xff0c;输入一个真分数把其分解为埃及分数之和。 2.设计思路&#xff1a;设分数为a/b,则cb/a,db%a,…

【数模】相关性分析

相关性&#xff1a;如果一个变量的变化引起了另一个变量的变化 目录 一、四种基本变量 二、 相关性分析方法 1.Pearson相关系数 2.Spearman 等级相关系数 3.Kendall tua-b 等级相关系数 4.卡方检测 5.Eta系数 *SPSS操作 三、偏相关 1.SPSS操作 2.偏相关系数和检验&…

数学建模-相关性分析及热力图

目录 一、相关性分析 二、相关性分析实例 三、三种相关系数 3.1 Pearson线性相关系数 3.2 Kendall tau系数 3.3 Spearman相关系数 4、Matlab代码 4.1 Pearson 显著性检验 4.2 Pearson 相关系数矩阵 4.3 Kendalltau相关系数矩阵 4.4 Spearman相关系数矩阵 5、代码…