0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C++多继承的二义性问题

NJ90_gh_bee81f8 来源:未知 作者:李建兵 2018-03-14 15:26 次阅读

在多重继承中,需要解决的主要问题是标识符不唯一,即二义性问题。比如,当在派生类继承的多个基类中有同名成员时,派生类中就会出现标识符不唯一的情况。

在多重继承中,派生类由多个基类派生时,基类之间用逗号隔开,且每个基类前都必须指明继承方式,否则默认是私有继承。可以通过以下3种方法解决二义性问题。

使用运算符"::";

使用同名覆盖的原则;

使用虚基类;

1.使用域运算符

如果派生类的基类之间没有继承关系,同时又没有共同的基类,则在引用同名成员时,可以在成员名前面加上类名和域运算符来区别来自不同基类的成员。

2.使用同名覆盖的原则

在派生类中重新定义与基类中同名的成员(如果是成员函数,则参数表也要相同,否则是重载)以屏蔽掉基类中的同名成员,在引用这些同名成员时,引用的就是派生类中的成员。

#include

using namespace std;

class Base

{

public:

int x;

void show()

{

cout << "Base,x= " << x << endl;

}

};

class Derived :public Base

{

public:

int x;

void show()

{

cout << "Derived , x= " << x << endl;

}

};

int main()

{

Derived ob;

ob.x = 5;

ob.show();

ob.Base::show();

ob.Base::x = 12;

ob.Base::show();

system("pause");

return 0;

}

3.虚基类

在多重继承中,要引用派生类的成员时,先是在派生类自身的作用域内寻找,如果找不到再到基类中寻找。这时,如果这些基类有一个共同的基类,派生类访问这个共同基类的成员时,就有可能由于同名成员的问题而发生二义性,此时就需要虚基类来解决。

#include

using namespace std;

class Base

{

public:

Base()

{

x = 1;

}

protected:

int x;

};

class Base1 :public Base

{

public:

Base1()

{

cout << "Base1,x= " << x << endl;

}

};

class Base2 :public Base

{

public:

Base2()

{

cout << "Base2,x= " << x << endl;

}

};

class Derived :public Base1, public Base2

{

public:

Derived()

{

cout << "Derived,x= " << x << endl;

}

};

int main()

{

Derived obj;

system("pause");

return 0;

}

上边的代码表面上看类Base1和类Base2是从同一个基类Base派生出来的,但是其对应的却是基类Base的两个不同的复制。因此,当派生类Derived要访问变量x时不知从哪条路径去寻找,从而引发二义性问题。

上述代码对应的类层次结构如图1所示,属于非虚基类的类层次结构。要解决该问题,需要引入虚基类,其具体的做法是将公共基类声明为虚基类,这样这个公共基类就只有一个拷贝,从而不会出现二义性问题。虚基类的类层次结构如图2所示。

图 1

图 2

3.1虚基类

虚基类的声明是在派生类的声明过程中进行的,其声明的一般形式为:

class<派生类名>:virtual <派生方式><基类名>

这种派生方式叫做虚拟继承,虚基类关键字的作用范围和派生方式与一般派生类的声明一样,只对紧跟其后的基类起作用。声明了虚基类以后,虚基类的成员在进一步的派生过程中和派生类一起维护同一个内存拷贝。

#include

using namespace std;

class Base

{

public:

Base()

{

x = 1;

}

protected:

int x;

};

class Base1 :virtual public Base

{

public:

Base1()

{

cout << "Base1,x= " << x << endl;

}

};

class Base2 :virtual public Base

{

public:

Base2()

{

cout << "Base2,x= " << x << endl;

}

};

class Derived :public Base1, public Base2

{

public:

Derived()

{

cout << "Derived,x= " << x << endl;

}

};

int main()

{

Derived obj;

system("pause");

return 0;

}

在上述代码中,由于把公共基类Base声明为类Base1和Base2的虚基类,所以由类Base1和类Base2派生的类Derived只有一个基类Base,消除了二义性。

3.2虚基类构造函数和初始化

虚基类的初始化与一般的多继承的初始化在语法上是一样的,但是构造函数的执行顺序不同。主要在以下方面:

虚基类的构造函数的执行在非虚基类的构造函数之前;

若同一层次中包含多个虚基类,这些虚基类的构造函数按照他们被声明的先后顺序执行;

若虚基类由非虚基类派生而来,则仍然先执行基类的构造函数,再执行派生类的构造函数。

#include

using namespace std;

class Base

{

public:

Base(int x1)

{

x = x1;

cout << "Base,x= " << x << endl;

}

protected:

int x;

};

class Base1 :virtual public Base

{

int y;

public:

Base1(int x1, int y1) :Base(x1)

{

y = y1;

cout << "Base1 ,y=" << y << endl;

}

};

class Base2 :virtual public Base

{

int z;

public:

Base2(int x1, int z1) :Base(x1)

{

z = z1;

cout << "Base2,z= " << z << endl;

}

};

class Derived :public Base1, public Base2

{

int xyz;

public:

Derived(int x1, int y1, int z1, int xyz1) :Base(x1), Base1(x1,y1), Base2(x1,z1)

{

xyz = xyz1;

cout << "Derived,xyz = " << xyz << endl;

}

};

int main()

{

Derived obj(1, 2, 3, 4);

system("pause");

return 0;

}

上边的代码中,虚基类Base的构造函数只执行了一次,这是因为当派生类Derived调用了虚基类Base的构造函数之后,类Base1和Base2对虚基类Base构造函数的调用就被忽略,这是初始化虚基类和初始化非虚基类的不同。

在使用虚基类时要注意:

虚基类的关键字virtual与派生方式的关键字public,private,protected的书写位置无关紧要,可以先写虚基类的关键字,也可以先写派生 方式的关键字;

一个基类在作为某些类的虚基类的同时可以作为另一些类的非虚基类;

虚基类构造函数的参数必须由最新派生出来的类负责初始化,即使不是直接继承也应如此。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    21

    文章

    2094

    浏览量

    73442

原文标题:C++多继承的二义性问题

文章出处:【微信号:gh_bee81f890fc1,微信公众号:面包板社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C++之STL算法()

    C++之STL算法()
    的头像 发表于 07-18 14:49 939次阅读
    <b class='flag-5'>C++</b>之STL算法(<b class='flag-5'>二</b>)

    【好资料系列】C++实验 继承与派生——教师工资计算

    C++实验继承与派生——教师工资计算.doc
    发表于 08-05 20:41

    【微信精选】C++的精髓类和继承就该这么理解!

    中的公有成员。C++继承 以下例子:定义了一个基类base,定义了一个子类sum,子类继承了基类base,#include class sum: public base{public: int add
    发表于 08-15 09:41

    C++ 继承类 虚基类分享

    /details/1027495131.继承派生类有多个基类或者虚基类,同一个类不能多次作为某个派生类的直接基类,但可以作为一个派生类的间接基类;class QUEUE: STACK, STACK{...
    发表于 07-02 06:54

    MC三维重建算法的二义性消除研究

    面绘制法进行三维重建是三维重建技术的主要方法,Marching Cubes(移动立方体)算法是经典的面绘制法。本文在剖析了MC 算法的基础上,针对其存在的二义性问题,给出了消除二义性
    发表于 01-22 12:02 21次下载

    C++内部类的实现及应用

    多重继承一直是C++语言中一个广受争议的语言特征。一般认为,在面向对象设计中应该谨慎使用多重继承。文章分析了C++中多重继承实现的复杂性并对
    发表于 06-28 17:59 50次下载
    <b class='flag-5'>C++</b>内部类的实现及应用

    继承与派生_实验3

    c++面向对象课程实验指导书题目_继承与派生。
    发表于 01-14 16:25 0次下载

    面向对象的程序设计(C++

    面向对象的程序设计(C++).面向对象的基本思想 C++C的非面向对象扩充 继承性与派生类 类库和基于C++的良好OOP风格。
    发表于 03-22 14:40 0次下载

    C++实验 继承与派生——教师工资计算

    C++实验 继承与派生——教师工资计算
    发表于 12-30 14:50 1次下载

    Visual C++教程之C++的基础知识介绍

    本文档的主要内容详细介绍的是Visual C++教程之C++的基础知识介绍主要内容包括了:1 类和对象,2 类的成员及特性,3 继承和派生类
    发表于 02-15 15:59 9次下载
    Visual <b class='flag-5'>C++</b>教程之<b class='flag-5'>C++</b>的基础知识介绍

    C++程序设计教程之继承的详细资料说明

    本文档的详细介绍的是C++程序设计教程之继承的详细资料说明主要内容包括了:1.继承结构 ( Inheritance Structure ) ,2.访问父类成员 ( Access Father’s
    发表于 02-22 11:24 98次下载
    <b class='flag-5'>C++</b>程序设计教程之<b class='flag-5'>继承</b>的详细资料说明

    C++中类的继承访问级别学习总结(

    上一篇文章我们介绍了c++中类的继承学习总结;今天我们继续来分享c++中类的继承中的访问级别的学习总结。一、继承中的访问级别学习:1、子类是
    的头像 发表于 12-24 16:10 695次阅读

    STM32 C++编程系列:STM32 C++代码封装初探

    一、STM32与面向对象编程上一章中提到了,C++的核心之一就在于面向对象思想。相比C语言常用的面向过程编程,面向对象编程的优势在于继承、封装、多态的特性,利用这种思想更有助于我们的程序实现模块化
    发表于 12-08 11:06 13次下载
    STM32 <b class='flag-5'>C++</b>编程系列<b class='flag-5'>二</b>:STM32 <b class='flag-5'>C++</b>代码封装初探

    使用Vivado Block Design设计解决项目继承性问题

    使用Vivado Block Design设计解决了项目继承性问题,但是还有个问题,不知道大家有没有遇到,就是新设计的自定义 RTL 文件无法快速的添加到Block Design中
    的头像 发表于 02-13 11:02 2999次阅读

    怎样在Java中实现继承

    Java是一种面向对象的只允许单继承的语言,那么怎样在Java中实现继承呢? 2 方法 1. 多层继承 如果要直接继承类,子类
    的头像 发表于 02-17 14:55 1272次阅读