您的位置:首页 > 编程语言 > C语言/C++

C++菱形继承模型刨析

2017-10-08 14:32 218 查看
继承概念:
继承机制是新的类从已有类那里得到已有的特性。亦或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。它是使代码可以复用的最重要的手段。
继承关系和访问限定符:
<单继承>
一个类只有一个直接父类时,称这个继承关系为单继承
#include<iostream>classBase{public:Base(intpub=0,intpro=1,intpri=2):_pub(pub),_pro(pro),_pri(pri){cout<<"B()"<<endl;}~Base(){cout<<"~B()"<<endl;}voidShowBase(){cout<<"_pri="<<_pri<<endl;cout<<"_pro="<<_pro<<endl;cout<<"_pub="<<_pub<<endl;}public:int_pub;protected:int_pro;private:int_pri;};classDerived:publicBase{public:Derived(intDpub=3,intDpro=4,intDpri=5):_Dpub(Dpub),_Dpro(Dpro),_Dpri(Dpri){cout<<"Derived()"<<endl;}~Derived(){cout<<"~Derived()"<<endl;}voidShowDerived(){cout<<"_pro="<<_pro<<endl;cout<<"_Dpri="<<_Dpri<<endl;cout<<"_Dpro="<<_Dpro<<endl;cout<<"_Dpub="<<_Dpub<<endl;}public:int_Dpub;protected:int_Dpro;private:int_Dpri;};voidtest0(){Derivedd1;d1.ShowBase();d1.ShowDerived();d1._pub=10;d1._Dpub=11;d1.ShowBase();d1.ShowDerived();}intmain(){test0();return0;}
一下是单继承模式下Derived类继承Base类的之后的模型。
基类的成员变量在上面,派生类成员变量在基类下面。
运行结果:
总结:
1.基类的private成员在派生类是不能被访问的(不可见),如果基类成员不想在类外直接被访问,但需要在派生中能访问,就应该定义为protected。就可以看出保护成员限定符是因为继承才出现的。
2.public继承是一个接口继承,保持is—a原则,每个父类可用的成员对子类也可用,因为每个子类对象也是一个父类对象。
3.protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是has-a的关系
原则。
4.不管那种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员不管在哪种继承方式下在派生类都是不可见的。
5.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public。(建议最好写出继承方式)
继承关系中构造函数的调用顺序:
编译器调用派生类构造函数---》转调基类构造函数---》再执行派生类构造函数体。
1.基类构造函数调用顺序按照继承列表中的顺序调用。
2.构造派生类类成员时,按照在派生类中声明的次序调用。
【说明】
1.基类没有缺省构造函数,派生类必须要在初始列表中显示调用基类的构造函数。
2.基类如果没有定义构造函数,派生类也可以选择不定义,全部使用缺省构造函数。
3.如果基类没有缺省构造函数,派生类必须要定义一个构造函数,在初始化列表中显示调用基类构造函数。
继承关系中析构函数的调用过程:
派生类析构函数---》派生类包含成员对象析构函数(调用顺序和成员对象在基类中声明的顺序相反)--》基类析构函数(调用顺序和基类在派生列表中的声明顺序相反)
继承体系的作用域:
#include<iostream>usingnamespacestd;classBase{public: voiddisplay() {  cout<<"父类函数"<<endl; }private:};classDeiverd:publicBase{public: voiddisplay() {  cout<<"子类函数"<<endl; }};intmain(){ Deiverdd1; d1.display(); return0;}
运行结果:
1.在继承体系中基类和派生类是两个不同的作用域。
2.子类和父类中有同名成员,子类成员将屏蔽对父类成员的直接访问。(在子类成员函数中,可以使用基类::基类成员访问)构成同名隐藏---重定义。(在实际应用中,除非必要否则应尽量避免定义同名成员)
(1)同名隐藏和函数重载:
继承与转换--赋值兼容规则--(必须是public):
1.子类对象可以赋值给父类对象(切割/切片)
2.父类对象不能赋值给子类对象
3.父类指针或引用可以指向子类对象
4.子类的指针或引用不能指向父类对象(可以通过强制类型转换完成)
友元和静态成员与继承的关系:
1.友元关系不能继承,也就是基类友元不能访问子类私有和保护(友元函数并不是成员函数,所以并不能继承下来)
2.基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例(静态成员是属于类的,是所有对象共享一个)
单继承&&多继承&&菱形继承
单继承已在上面说明,我们看一下多继承的模型,一个类有两个及两个以上的直接父类时,称这个继承为多继承。
#include<iostream>usingnamespacestd;classBase{public: Base(intpub=0,intpri=1):_pub(pub),_pri(pri) {  cout<<"Base()"<<endl; } int_pub;private: int_pri;};classBase0{public: Base0(intpub0=2,intpri0=3):_pub0(pub0),_pri0(pri0) {  cout<<"Base0()"<<endl; } int_pub0;private: int_pri0;};classDeiverd:publicBase,publicBase0{public: Deiverd(intDpub=4,intDpri=5):_Dpub(Dpub),_Dpri(Dpri) {  cout<<"Deiverd()"<<endl; } int_Dpub;private: int_Dpri;};voidtest0(){ Deiverdd1;}intmain(){ test0(); return0;}
继承模型如下:
菱形继承:
#include<iostream>usingnamespacestd;classBase{public: Base(intpub=0,intpri=1):_pub(pub),_pri(pri) {  cout<<"Base()"<<endl; } int_pub;private: int_pri;};classDeiverd0:publicBase{public: Deiverd0(intDpub0=2,intDpri0=3):_Dpub0(Dpub0),_Dpri0(Dpri0) {  cout<<"Deiverd0()"<<endl; } int_Dpub0;private: int_Dpri0;};classDeiverd:publicBase{public: Deiverd(intDpub=4,intDpri=5):_Dpub(Dpub),_Dpri(Dpri) {  cout<<"Deiverd()"<<endl; } int_Dpub;private: int_Dpri;};classFinal:publicDeiverd0,publicDeiverd{public: Final(intfpub=7,intfpri=8):_fpub(fpub),_fpri(fpri) {  cout<<"Final()"<<endl; } int_fpub;private: int_fpri;};voidtest0(){ Finalf1; //f1._pub=5; f1.Deiverd::_pub=5;}intmain(){ test0(); return0;}
菱形继承模型图分析:
根据内存分布情况画出抽象图:
通过上图可以看出基类Base在Final创建的对象里面一共保存了两份。当你尝试用此语句:f1._pub=5;时编译器会报错
因为编译器并不知道你要访问是那一个_pub,是Deiverd继承来的?亦或是Deiverd0继承来的,所以编译器只能给你报出Final::_pub不明确;但是你可以通过语句:f1.Deiverd::_pub=5;或者f1.Deiverd0::_pub=5;来访问_pub。
菱形继承总结:
1.菱形继承存在明显的二义性。
2.菱形继承存在数据的冗余,造成空间的大量浪费。
3.在设计时尽量不要用菱形继承。
以上就是我关于继承部分的理解,关于菱形虚拟继承将在下一部分进行刨析,如果有那里不对的地方,还望不吝赐教,在下方留言或者私信我。
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: