C++菱形继承模型刨析

继承概念:
继承机制是新的类从已有类那里得到已有的特性。亦或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。它是使代码可以复用的最重要的手段。


继承关系和访问限定符:


 
<单继承>
一个类只有一个直接父类时,称这个继承关系为单继承

#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.在设计时尽量不要用菱形继承。

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值