-
2019-11-20 23:07:09
实际的业务场景中,一些事物会有多个属性。由此c++引入了,多重继承的概念,也就是允许一个派生类指定多个基类,这样就被叫做多重继承。
如下代码:
#include "stdafx.h" #include <iostream> #pragma warning(disable:4996) #include <string> using namespace std; class car{ public: car(){ cout << "this is car" << endl; } void print(void) { cout << "car class print" << endl; } }; class truck:public car { public: truck(){ cout << "this is truck" << endl; } void truck_print(void){ cout << "truck::truck_print" << endl; } }; class bus:public car { public: bus(){ cout << "this is bus" << endl; } void bus_print(void){ cout << "bus::bus_print" << endl; } }; class train :public truck, public bus{ public: train(){ cout << "this is train" << endl; } void train_print(void){ cout << "train::train_print" << endl; } }; int main(int argc, char *argv[]) { train test1;//多重继承的构造顺序和继承列表中基类的排列顺序一致,不是和它的初始化列表中的顺序 //一致,所以先执行truck中的构造函数,truck先执行car的构造函数。再执行bus中的构造函数,bus中先执行 //car中的构造函数 /*this is car this is truck this is car this is bus this is train*/ test1.train_print();//train::train_print test1.truck_print();//truck::truck_print test1.print();//出现二义性,print成员函数不明确。因为类bus和truck继承car的时候,都分别对 //car中的成员函数print做了一份拷贝。train在继承bus和truck时对bus和truck多了拷贝。所以 //test1.print()无法访问那个基类的print。 car *p_car = &test1;//同样会产生二义性,将上面的继承按照下面的方法二,改为虚继承即可。 return 0; }
消除二义性的方法:
1.加上全局符指定使用哪一份拷贝。如:
test1.truck::print(); test1.bus::print();
2.使用虚继承,这样的话。派生类train只有一份基类car的拷贝了。如:
将上面的
class truck:public car
class bus:public car
改为:
class truck:virtual public car
class bus:virtual public car
更多相关内容 -
C++多重继承二义性原理实例解析
2020-08-19 00:09:26主要介绍了C++多重继承二义性原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
C++基础知识 - 多重继承的二义性问题
2022-02-17 20:57:30多重继承的二义性就是在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性——同名二义性 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,...什么是二义性?
- 简单来说就是一个班级有两个同名的人都叫张三, 老师叫张三回答这道题, 这时, 谁都不知道老师指的是哪个张三, 这就是二义
- 多重继承的二义性就是在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性——同名二义性
- 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生另一种不确定性——路径二义性。
demo
#include <iostream> #include <Windows.h> using namespace std; class Father { public: Father() {} ~Father() {} void play() {cout << "Father KTV唱歌!" << endl;} }; class Mother { public: Mother() {} ~Mother() {} void play() {cout << "Mother 逛街购物!" << endl;} }; class Son : public Father, public Mother{ public: Son() {} ~Son() {} }; int main(void) { Son son; son.play(); //无法确定该调用Fther的play()还是Mother的play() system("pause"); return 0; }
解决多重继承的二义性的方法1:
使用 “类名::” 进行指定, 指定调用从哪个基类继承的方法!
son.Father::play(); son.Mother::play();
解决多重继承的二义性的方法2:
在子类中重新实现这个同名方法, 并在这个方法内部, 使用基类名进行限定,
来调用对应的基类方法class Son : public Father, public Mother{ public: Son() {} ~Son() {} void play() { Father::play(); Mother::play(); } }; int main(void) { Son son; son.play(); //调用自己类内部重写的play()方法 system("pause"); return 0; }
-
C++多继承中的二义性问题
2018-05-24 17:35:46在C++中,派生类继承基类,对基类成员的访问应该是确定的、唯一的,但是常常会有以下情况导致访问不一致,产生二义性。 1.在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性——...在C++中,派生类继承基类,对基类成员的访问应该是确定的、唯一的,但是常常会有以下情况导致访问不一致,产生二义性。1.在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性——同名二义性。2.当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生另一种不确定性——路径二义性。
1.
“倒三角”问题——同名二义性
#include "iostream" using namespace std; class Parent_f { public: void show() { cout<<"This is Parent_f\n"; } }; class Parent_m { public: void show() { cout<<"This is Parent_m\n"; } }; class Son:public Parent_f,public Parent_m { public: void display() { cout<<"This is son\n"; } }; int main() { Son son; son.show(); son.display(); cout << "Hello world!" << endl; return 0; }
上面的代码中,2个父类派生一个子类,但两个父类中都有同名的成员函数。派生出的子类产生二义性问题,编译时会报:
error: request for member 'show' is ambiguous
解决方法:
(1)利用作用域限定符(::),用来限定子类调用的是哪个父类的show()函数
(2)在类中定义同名成员,覆盖掉父类中的相关成员son.Parent_f::show();
class Son:public Parent_f,public Parent_m { public: void display() { cout<<"This is son\n"; } void show() { cout<<"show:This is son.\n"; } };
2.
“菱形”问题——路径二义性
有最基类A,有A的派生类B、C,又有D同时继承B、C,那么若A中有成员a,那么在派生类B,C中就存在a,又D继承了B,C,那么D中便同时存在B继承A的a和C继承A的a,那么当D的实例调用a的时候就不知道该调用B的a还是C的a,就导致了二义性。
#include "iostream" using namespace std; class Grandpa { public: int year_old; }; class Parent_f:public Grandpa {}; class Parent_m:public Grandpa {}; class Son:public Parent_f,public Parent_m {}; int main() { Son son; son.year_old = 10; cout << "Hello world!" << endl; return 0; }
Grandpa为Parent_f和Parent_m的基类,而Son又继承Parent_f和Parent_m,当Son访问Grandpa的year_old时,会出现二义性错误.
解决方法:
(1)使用作用域限定符,指明访问的是哪一个基类的成员。
注意:不能是Grandpa作用域下限定,因为Son直接基类的基类才是Grandpa,纵使指明了访问Grandpa的成员的话,对于Son来说,还是模糊的,还是具有二义性。
(2)在类中定义同名成员,覆盖掉父类中的相关成员。
(3)虚继承、使用虚基类
教科书上面对C++虚基类的描述玄而又玄,名曰“共享继承”,名曰“各派生类的对象共享基类的的一个拷贝”,其实说白了就是解决多重多级继承造成的二义性问题。父类对祖父类的继承方式改为虚继承,那么子类访问自己从祖父类那里继承过来的成员就不会有二义性问题了,也就是将子类对象里的祖父类对象统一为一个,继承的只有一个祖父类对象,代码如下。
#include "iostream" using namespace std; class Grandpa { public: int year_old; void show() { cout << "year_old:" << year_old <<endl; } }; class Parent_f:virtual public Grandpa {}; class Parent_m:virtual public Grandpa {}; class Son:public Parent_f,public Parent_m {}; int main() { Grandpa grp; Parent_f pa_f; Parent_m pa_m; Son son; grp.year_old = 100; pa_f.year_old = 55; pa_m.year_old = 50; son.year_old = 10; grp.show(); pa_f.show(); pa_m.show(); son.show(); cout << "Hello world!" << endl; return 0; }
使用了虚基类的方法,是不是感觉简单方便多了呢
-
C++多重继承及多态性原理实例详解
2020-08-18 22:47:02主要介绍了C++多重继承及多态性原理实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
C++ 多重继承
2021-01-20 13:39:42注意:多重继承在实际开发中尽量少用,能不用就不用。多重继承很繁杂,很复杂。 多重继承就是派生类继承多个基类,继承方法和一个继承是一样的。 一个继承请看我 —> “C++ 继承和派生” 文章,会有详细介绍。 链接:... -
C++ 多重继承详解
2022-02-21 19:52:42C++ 多重继承多重继承的语法多重继承下的构造函数多重继承下的命名冲突派生类对象中数据成员的排列形式 多重继承的语法 多重继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A、类B和类C,那么可以...多重继承的语法
多重继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A、类B和类C,那么可以这样来声明派生类D:
class D: public A, private B, protected C{ //类D新增加的成员 }
D 是多继承形式的派生类,它以公有的方式继承 A 类,以私有的方式继承 B 类,以保护的方式继承 C 类。D 根据不同的继承方式获取 A、B、C 中的成员,确定它们在派生类中的访问权限。
多重继承下的构造函数
多继承形式下的构造函数和单继承形式基本相同,只是要在派生类的构造函数中调用多个基类的构造函数。以上面的 A、B、C、D 类为例,D 类构造函数的写法为:
D(形参列表): A(实参列表), B(实参列表), C(实参列表){ //其他操作 }
基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关,而是和声明派生类时基类出现的顺序相同。仍然以上面的 A、B、C、D 类为例,即使将 D 类构造函数写作下面的形式:
D(形参列表): B(实参列表), C(实参列表), A(实参列表){ //其他操作 }
那么也是先调用 A 类的构造函数,再调用 B 类构造函数,最后调用 C 类构造函数。
下面是一个多继承的实例:
#include <iostream> using namespace std; //基类 class BaseA{ public: BaseA(int a, int b); ~BaseA(); protected: int m_a; int m_b; }; BaseA::BaseA(int a, int b): m_a(a), m_b(b){ cout<<"BaseA constructor"<<endl; } BaseA::~BaseA(){ cout<<"BaseA destructor"<<endl; } //基类 class BaseB{ public: BaseB(int c, int d); ~BaseB(); protected: int m_c; int m_d; }; BaseB::BaseB(int c, int d): m_c(c), m_d(d){ cout<<"BaseB constructor"<<endl; } BaseB::~BaseB(){ cout<<"BaseB destructor"<<endl; } //派生类 class Derived: public BaseA, public BaseB{ public: Derived(int a, int b, int c, int d, int e); ~Derived(); public: void show(); private: int m_e; }; Derived::Derived(int a, int b, int c, int d, int e): BaseA(a, b), BaseB(c, d), m_e(e){ cout<<"Derived constructor"<<endl; } Derived::~Derived(){ cout<<"Derived destructor"<<endl; } void Derived::show(){ cout<<m_a<<", "<<m_b<<", "<<m_c<<", "<<m_d<<", "<<m_e<<endl; } int main(){ Derived obj(1, 2, 3, 4, 5); obj.show(); return 0; }
运行结果:
BaseA constructor BaseB constructor Derived constructor 1, 2, 3, 4, 5 Derived destructor BaseB destructor BaseA destructor
从运行结果中还可以发现,多继承形式下析构函数的执行顺序和构造函数的执行顺序相反。
多重继承下的命名冲突
当两个或多个基类中有同名的成员时,如果直接访问该成员,就会产生命名冲突,编译器不知道使用哪个基类的成员。这个时候需要在成员名字前面加上类名和域解析符::,以显式地指明到底使用哪个类的成员,消除二义性。
修改上面的代码,为 BaseA 和 BaseB 类添加 show() 函数,并将 Derived 类的 show() 函数更名为 display():
#include <iostream> using namespace std; //基类 class BaseA{ public: BaseA(int a, int b); ~BaseA(); public: void show(); protected: int m_a; int m_b; }; BaseA::BaseA(int a, int b): m_a(a), m_b(b){ cout<<"BaseA constructor"<<endl; } BaseA::~BaseA(){ cout<<"BaseA destructor"<<endl; } void BaseA::show(){ cout<<"m_a = "<<m_a<<endl; cout<<"m_b = "<<m_b<<endl; } //基类 class BaseB{ public: BaseB(int c, int d); ~BaseB(); void show(); protected: int m_c; int m_d; }; BaseB::BaseB(int c, int d): m_c(c), m_d(d){ cout<<"BaseB constructor"<<endl; } BaseB::~BaseB(){ cout<<"BaseB destructor"<<endl; } void BaseB::show(){ cout<<"m_c = "<<m_c<<endl; cout<<"m_d = "<<m_d<<endl; } //派生类 class Derived: public BaseA, public BaseB{ public: Derived(int a, int b, int c, int d, int e); ~Derived(); public: void display(); private: int m_e; }; Derived::Derived(int a, int b, int c, int d, int e): BaseA(a, b), BaseB(c, d), m_e(e){ cout<<"Derived constructor"<<endl; } Derived::~Derived(){ cout<<"Derived destructor"<<endl; } void Derived::display(){ BaseA::show(); //调用BaseA类的show()函数 BaseB::show(); //调用BaseB类的show()函数 cout<<"m_e = "<<m_e<<endl; } int main(){ Derived obj(1, 2, 3, 4, 5); obj.display(); return 0; }
注意第 64、65 行代码,我们显式的指明了要调用哪个基类的 show() 函数。
派生类对象中数据成员的排列形式
在普通多继承方式下,若定义派生类对象,其中的数据成员的排列规则是:首先按照派生类定义形式中基类的排列顺序,将基类成员依次排列,接下来再存放派生类中新添加的数据,假设有如下代码:
class Base1{ //定义基类Base1 public: void func_base1(); private: int n_base1; int m_base1; }; class Base2{ //定义基类Base2 public: void func_base2(); private: int n_base2; int m_base2; }; //定义派生类Derive,继承自Base1和Base2 class Derive:public Base1, public Base2{ public: void func_derive(); private: int n_derive; };
上述代码中,派生 Derive 继承自基类 Base1 和 Base2,若定义派生类对象,则其中的数据成员排列形式如下图所示。
由图 2 可以看出,对于派生类 Derive 的对象来说,它将基类 Base1 的成员排列在最前端,第二个基类 Base2 的数据紧随其后,派生类自身的成员在最后存放,这种排列方式,使得不同类型的指针变量访问数据时操作比较简单,只需要从起始址开始,改变固定的偏移量即可。
-
C++多重继承与虚继承分析
2021-01-01 12:44:23本文以实例形式较为全面的讲述了C++的多重继承与虚继承,是大家深入学习C++面向对象程序设计所必须要掌握的知识点,具体内容如下: 一、多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类... -
C++ 多重继承和虚拟继承对象模型、效率分析
2021-01-01 11:24:14C++多态通过继承和动态绑定实现。继承是一种代码或者功能的传承共享,从语言的角度它是外在的、形式上的,极易理解。而动态绑定则是从语言的底层实现保证了多态的发生——在运行期根据基类指针或者引用指向的真实... -
C++实现的多重继承功能简单示例
2020-08-27 12:23:45主要介绍了C++实现的多重继承功能,结合简单实例形式分析了C++面向对象程序设计中类的定义与继承相关操作实现技巧,需要的朋友可以参考下 -
说说C++多重继承
2021-01-20 03:40:35多重继承是从多于一个直接基类派生类的能力,多重继承的派生类继承其父类的属性。 class ZooAnimal{ }; class Bear : public ZooAnimal{ }; class Endangered{ }; class Panda : public ... -
C++多继承的二义性问题.pdf
2020-10-30 02:12:26C++ 多继承的二义性问题 在多重继承中需要解决的主要问题是标识符不唯一即二义性问题比如 当在派生类继承的多个基类中有同名成员时派生类中就会出现标识符不唯一的情况 在多重继承中派生类由多个基类派生时基类之间... -
C++习题 多重继承
2016-07-07 12:59:31c++多重继承的一个习题 -
C++中的多态与多重继承实现与Java的区别
2020-08-19 11:14:36主要介绍了C++中的多态与多重继承实现与Java的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
C++ 多继承和多重继承
2021-11-10 15:30:11一、多重继承 多重继承是一个类的父类也有父类,一层一层的继承父类。 多重继承的对象初始化方式最高的父类到子类。 A()–>B–>C() 销毁时正好是初始化的反顺序。 ~C–>~B–>A() class A{}; class B:... -
C++多重继承引发的重复调用问题与解决方法
2020-08-27 12:39:10主要介绍了C++多重继承引发的重复调用问题与解决方法,结合具体实例形式分析了C++多重调用中的重复调用问题及相应的解决方法,需要的朋友可以参考下 -
C++多继承(多重继承)详解
2022-02-08 10:32:04C++多继承(多重继承)详解 在前面的例子中,派生类都只有一个基类,称为单继承(Single Inheritance)。除此之外,C++也支持多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承容易让代码... -
c++多重继承
2014-05-03 09:48:10类的多重继承演示"<<endl; CDateTimeType dt 1 1 2008 11 12 12 ; 直接使用DateTimeType构造函数设置日期时间 cout<<"调用CDateTimeType类构造函数设定的初始日期 时间为:"<<... -
C++多继承(多重继承)详解(二)命名冲突
2021-11-07 20:02:50命名冲突 当两个或多个基类中有同名的成员时,如果直接访问该成员,就会产生命名冲突,编译器不知道使用哪个...这个时候需要在成员名字前面加上类名和域解析符::,以显式地指明到底使用哪个类的成员,消除二义性。 ... -
C++多继承中二义性的解决方案
2018-05-27 15:30:24出现二义性的原因:派生类在访问基类成员函数时,由于基类存在同名的成员函数,...1. 什么是多重继承的二义性 class A{ public: void f(); } class B{ public: void f(); void g(); } class C:public A,pub... -
C++的多重继承
2020-06-16 18:06:172,C++ 支持编写多重继承的代码: 1,一个子类可以拥有多个父类; 2,子类拥有所有父类的成员变量; 3,子类继承所有父类的成员函数; 4,子类对象可以当作任意父类对象使用; 3,多重继承的语法规则: 1,代码... -
类的继承第二次作业(多重继承与虚基类)参考答案_C++_teacher_
2021-09-29 10:36:26定义一个Person 类,结构如下图,为该类添加输入输出流运算符重载。Person 类以Public方式派生出一个Teacher类,数据成员包括:int gh(工号);char* title(职称),为该类添加输入输出流运算符重载,完成有参、无参、...