C++菱形继承模型刨析

来源:互联网 发布:淘宝店招模板制作 编辑:程序博客网 时间:2024/05/20 09:11
继承概念:
继承机制是新的类从已有类那里得到已有的特性。亦或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。它是使代码可以复用的最重要的手段。
继承关系和访问限定符:
<单继承>
一个类只有一个直接父类时,称这个继承关系为单继承
#include<iostream>class Base{public:Base(int pub = 0, int pro = 1, int pri = 2):_pub(pub),_pro(pro),_pri(pri){cout << "B()" << endl;}~Base(){cout << "~B()" << endl;}void ShowBase(){cout << "_pri = " << _pri << endl;cout << "_pro = " << _pro << endl;cout << "_pub = " << _pub << endl;}public:int _pub;protected:int _pro;private:int _pri;};class Derived :public Base{public:Derived(int Dpub = 3,int Dpro=4,int Dpri =5):_Dpub(Dpub),_Dpro(Dpro),_Dpri(Dpri){cout << "Derived()" << endl;}~Derived(){cout << "~Derived()" << endl;}void ShowDerived(){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;};void test0(){Derived d1;d1.ShowBase();d1.ShowDerived();d1._pub = 10;d1._Dpub = 11;d1.ShowBase();d1.ShowDerived();}int main(){test0();return 0;}
一下是单继承模式下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>using namespace std;
class Base{public: void display() {  cout << "父类函数" << endl; }
private:
};class Deiverd:public Base{public: void display() {  cout << "子类函数" << endl; }};
int main(){ Deiverd d1; d1.display(); return 0;}
运行结果:
 
1.在继承体系中基类和派生类是两个不同的作用域。
2.子类和父类中有同名成员,子类成员将屏蔽对父类成员的直接访问。(在子类成员函数中,可以使用基类::基类成员 访问)构成同名隐藏---重定义。(在实际应用中,除非必要否则应尽量避免定义同名成员)
(1)同名隐藏和函数重载:
继承与转换--赋值兼容规则--(必须是public):
1.子类对象可以赋值给父类对象(切割/切片)
2.父类对象不能赋值给子类对象
3.父类指针或引用可以指向子类对象
4.子类的指针或引用不能指向父类对象(可以通过强制类型转换完成)
友元和静态成员与继承的关系:
1.友元关系不能继承,也就是基类友元不能访问子类私有和保护(友元函数并不是成员函数,所以并不能继承下来)
2.基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例(静态成员是属于类的,是所有对象共享一个)
单继承&&多继承&&菱形继承
单继承已在上面说明,我们看一下多继承的模型,一个类有两个及两个以上的直接父类时,称这个继承为多继承。
#include<iostream>using namespace std;
class Base{public: Base(int pub = 0, int pri = 1) :_pub(pub), _pri(pri) {  cout << "Base()" << endl; } int _pub;private: int _pri;
};class Base0{public: Base0(int pub0 = 2, int pri0 = 3) :_pub0(pub0), _pri0(pri0) {  cout << "Base0()" << endl; } int _pub0;private: int _pri0;};class Deiverd:public Base,public Base0{public: Deiverd(int Dpub = 4, int Dpri = 5) :_Dpub(Dpub), _Dpri(Dpri) {  cout << "Deiverd()" << endl; } int _Dpub;private: int _Dpri;};void test0(){ Deiverd d1;}int main(){ test0(); return 0;}
继承模型如下:
菱形继承:
#include<iostream>using namespace std;
class Base{public: Base(int pub = 0, int pri = 1) :_pub(pub), _pri(pri) {  cout << "Base()" << endl; } int _pub;private: int _pri;
};class Deiverd0:public Base{public: Deiverd0(int Dpub0 = 2, int Dpri0 = 3) :_Dpub0(Dpub0), _Dpri0(Dpri0) {  cout << "Deiverd0()" << endl; } int _Dpub0;private: int _Dpri0;};class Deiverd:public Base{public: Deiverd(int Dpub = 4, int Dpri = 5) :_Dpub(Dpub), _Dpri(Dpri) {  cout << "Deiverd()" << endl; } int _Dpub;private: int _Dpri;};class Final:public Deiverd0,public Deiverd{public: Final(int fpub = 7, int fpri = 8) :_fpub(fpub), _fpri(fpri) {  cout << "Final()" << endl; } int _fpub;private: int _fpri;};void test0(){ Final f1; //f1._pub = 5; f1.Deiverd::_pub = 5;}int main(){ test0(); return 0;}
菱形继承模型图分析:
根据内存分布情况画出抽象图:
通过上图可以看出基类Base在Final创建的对象里面一共保存了两份。                                 当你尝试用此语句:f1._pub=5;时编译器会报错
                                                       因为编译器并不知道你要访问是那一个_pub,是Deiverd继承来的?亦或是Deiverd0继承来的,所以编译器只能给你报出Final::_pub不明确;但是你可以通过语句:f1.Deiverd::_pub = 5;或者f1.Deiverd0::_pub = 5;来访问_pub。
菱形继承总结:
1.菱形继承存在明显的二义性。
2.菱形继承存在数据的冗余,造成空间的大量浪费。
3.在设计时尽量不要用菱形继承。

以上就是我关于继承部分的理解,关于菱形虚拟继承将在下一部分进行刨析,如果有那里不对的地方,还望不吝赐教,在下方留言或者私信我。


原创粉丝点击