繼承那些事兒

學過C語言得人應該知道C++的三大特點:繼承,封裝,多態。

今天先來聊聊第一大特點  繼承。

一、繼承的概念

什麼叫繼承呢?就是面向對象程序設計代碼可以複用的一種機制,手段。並且可以在保持原有類的特性上進行擴展,增加功能,這樣產生得新的類,就叫做派生類。

通俗的說派生類就是子類,兒它所繼承的基類又叫做父類。


二、繼承關係

(1)由繼承的定義格式可知,繼承有三大類:public(公有繼承)、protected(保護繼承)、private(私有繼承)。

代碼舉例:

class Base
{
public:
	int pub;
protected:
	int pro;
private:
	int pri;
};
class Derived :public/*protected*//*private*/Base
{
public:
	int _pub;
};
但需要注意的是,基類的private成員在派生類中是不能被訪問的。

繼承關係的圖例:

(2)繼承的小總結

1>基類的private成員在派生類中是不能被訪問的.

2>public繼承是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用,因爲每個子類對象也都是一個父類對象

3>protected/private繼承是一個實現繼承,基類的部分成員並非完全成爲子類接口的一部分,是 has-a 的關係原則,所以非特殊情況下不會使用這兩種繼承關係,在絕大多數的

場景下使用的都是公有繼承。

4>不管是哪種繼承方式,在派生類內部都可以訪問基類的公有成員和保護成員,基類的私有成員存在但是在子類中不可見(不能訪問)。

5>使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式。

三、派生類

1)繼承關係中構造函數調用順序


需要說明的是:

1>基類沒有缺省構造函數,派生類必須要在初始化列表中顯式給出基類名和參數列表。(基類中沒有,派生類中必須給出)

2>基類沒有定義(帶參數/無缺省值)構造函數,則派生類也可以不用(可定義也可不定義)定義,全部使用缺省構造函數。

3>基類定義了帶有形參表構造函數,派生類就一定定義構造函數。

而析構函數調用過程正好與構造函數相反。


下面用簡單例子來證明:

#include<iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "基類構造" << endl;
	}
	~Base()
	{
		cout << "基類析構" << endl;
	}

	int pub;
protected:
	int pro;
private:
	int pri;
};
class Derived :public Base
{
public:
	Derived()
		:Base()
		, d_pub()
	{
		cout << "派生類構造" << endl;
	}
	~Derived()
	{
		cout << "派生類析構" << endl;
	}

private:
	int d_pub;
};
void test()
{
	Derived d;
}
int main()
{
	test();
	return 0;
}

運行結果如下:

四、繼承體系中的作用域

1)在繼承體系中,基類和派生類是兩個不同作用域。

2)子類和父類有同名成員時,通過派生類對象訪問時優先訪問派生類中的成員,要想訪問基類中成員必須加作用域限定符。

同名隱藏:在繼承體系中,如果基類和派生類有同名成員,如果使用派生類對象調用基類和派生類的同名成員,優先調用派生類同名成員。

函數重載:相同作用域,函數名相同,參數列表不同。


五、繼承與轉換——賦值兼容規則

1)繼承模型

1>單繼承:一個子類只有一個父類。

2>多繼承:一個子類有多個父類。

class  B1
{
public:
	int _b1;
};
class B2
{
public:
	int _b2;
};
//單繼承
class A :public B1
{
public:
	int _a;
};
//多繼承
class D :public B1,public B2
{
public:
	int _d;
};
void test()
{
	A a;
	D d;
}
int main()
{
	test();
	return 0;
}
單繼承和多繼承的繼承模型都爲:基類在上,派生類在下

3>菱形繼承

2)繼承的二義性(虛繼承)



代碼如下:

class  B
{
public:
	int _b;
};
class C1:public B
{
public:
	int _c1;
};
class C2:public B
{
public:
	int _c2;
};
class D :public C1,public C2
{
public:
	int _d;
};
int main()
{
	D d;
	d._b;
    return 0;
}

要解決二義性,這就要談到虛繼承,即在公有繼承錢加上關鍵字virtual就解決了

class  B
{
	
public:
	B() :_b(0)
	{

	}
	int _b;
};
class C1 :virtual public B
{
	
public:
	C1() :_c1(1)
	{

	}
	int _c1;
};
class C2 :virtual public B
{
	
public:
	C2() :_c2(2)
	{

	}
	int _c2;
};
class D :public C1,public C2
{
	
public:
	D() :B(),_d(3)
	{

	}
	int _d;
};
int main()
{
	D d;
	d._b;
    return 0;
}
運行後,通過監視窗口觀察:



簡單的來說就是:                                                                                                                                         偏移量表格


3)友元關係不能繼承。

六、虛擬繼承與普通繼承的區別

1)書寫形式——虛擬關鍵字

2)對象模型——多了4個字節—地址——偏移量表格

普通繼承:基類部分在前,普通類在後

虛擬繼承:基類部分在最底下

3)對於繼承成員訪問形式

普通繼承:直接訪問

虛擬繼承:偏移量表格——相對於基類對象的偏移量

4)訪問基類成員——構造函數不同:1>派生類----合成構造函數-----將偏移量表格地址存放在對象前四個字節

                                                                   2>多一個參數--1—檢測是否爲虛擬繼承

好了,以上就是我對繼承的一點簡單總結,請大神們多多指教。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章