1. 程式人生 > >C++繼承部分總結

C++繼承部分總結

    C++是一門面向物件的語言,所以它的大部分操作都與類和方法息息相關,而C++語言也具有三大特性:封裝、繼承和多型。剛好我學習到的內容涉及到了繼承,為了防止遺忘在此進行一下相關的總結。

    繼承從字面上的意思來看就是子承父業,繼承首先需要的是一個子類和一個父類。從一個類派生出另一個類時,原始類稱為基類,繼承類稱為派生類。為說明繼承首先需要一個基類。

class A
{
public:
	A()
	{

		cout<<"establish:A()"<<endl;
	}
	~A()
   {
	   cout<<"des A()"<<endl;
	}
	int _a=2;
};
class B:virtual public A
{
	public:
	B()
	{

		cout<<"establish:B()"<<endl;
	}
	~B()
   {
	   cout<<"des B()"<<endl;
	}
	int _b=3;
};
int main()
{
    B b;
    cout<<b._a<<endl;
    return 0;
}

   列印結果是2。

   由此我們可以看出一點,B中有A的成員_a,在這個簡單的函式中,我們把A稱為基類,B繼承了A我們把B類稱為派生類,因為B使用的是A的部分成員。這樣做的目的就是為了減少重複的工作量。在B中我們可以看到屬於B特有的成員_b。在C++中,所謂繼承就是在一個已有類的基礎上建立一個新的類。已存在的類稱為基類,或父類。新建立的類稱為“派生類”或者子類,類的每一次派生都繼承了其基類的基本特徵,同時又根據需要調整和擴充原有的特徵。

  一個派生類不僅可以從一個基類派生,也可以從多個基類派生,也可以說一個派生類可以有兩個或多個基類。

    關於基類和派生類的關係,簡單來講就是:派生類是基類的具體化,而基類則是派生類的抽象。

    接著來講繼承,繼承有三種繼承方式,公有繼承,私有繼承和保護繼承。

    繼承的函式寫法如下:

     class 派生類名:[繼承方式]基類名

     {

        派生類新增加成員

       };

     三種繼承方式也有著各自不同的特點:

    然而私有繼承和保護繼承在我們正常使用中並非經常被使用到,因為私有繼承和保護繼承在使用中經常容易被搞錯,所以使用時一定要格外注意。

     使用三種繼承方式時需要注意以下幾個方面:

     1. 基類的private成員在派生類中是不能被訪問的,如果基類成員不想在類外直接被訪問,但需要 在派生類中能訪問,就定義為protected。可以看出保護成員限定符是因繼承才出現的。

     2. public繼承是一個介面繼承,保持is-a原則,每個父類可用的成員對子類也可用,因為每個子類 物件也都是一個父類物件。

     3. protected/private繼承是一個實現繼承,基類的部分成員並非完全成為子類介面的一部分, 是 has-a 的關係原則,所以非特殊情況下不會使用這兩種繼承關係,在絕大多數的場景下使用的 都是公有繼承。私有繼承以為這is-implemented-in-terms-of(是根據……實現的)。通常比 組合(composition)更低階,但當一個派生類需要訪問基類保護成員或需要重定義基類的虛函 數時它就是合理的。

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

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

      誠然,類中擁有的建構函式,解構函式等在繼承中同樣也會被呼叫。只不過在其中有著些許的不同。函式編譯時,先按照繼承列表中的順序呼叫基類建構函式,然後再呼叫派生類中物件建構函式,最後呼叫派生類建構函式體。而在繼承關係中解構函式呼叫過程,首先呼叫派生類的解構函式,再呼叫派生類包含成員物件解構函式,最後呼叫基類解構函式。  但是需要注意的是,基類沒有預設建構函式,派生類必須要在初始化列表中顯式給出基類名和引數列表。如果基類沒有定義建構函式,則派生類也可以不用定義,全部使用預設建構函式。  而如果基類定義了帶有形參表建構函式,派生類就一定定義建構函式。

    在各種各樣的繼承中,我們會發現許多問題,首先就是同名問題,如果在基類和派生類中存在同名函式,子類成員將遮蔽父類成員的直接訪問,因為在繼承體系中存在這不同的作用域,基類和派生類是兩個不同的作用域,派生類在訪問函式時,會隱藏基類的同名函式,這就是函式的同名隱藏。說到訪問,同樣需要注意的是子類物件可以賦值給父類物件,但是父類物件不可以給子類物件賦值(畢竟先構造基類,基類已經構造好如何再去賦值)。父類的指標(引用)可以指向子類物件,同理子類的指標不可以指向父類物件。

    繼承的使用大大縮減了我們對於一些重複工程花費的有用時間,但是多重繼承也會帶來一些問題,它大大地增加了程式的複雜程度,使程式的編寫和維護出現了許多未知的問題。最為顯著的問題就是二義性的問題。例如:

#include<iostream>
using namespace std;
class A
{
public:
	A()
	{

		cout<<"establish:A()"<<endl;
	}
	~A()
   {
	   cout<<"des A()"<<endl;
	}
	int _a;
};
class B:virtual public A
{
	public:
	B()
	{

		cout<<"establish:B()"<<endl;
	}
	~B()
   {
	   cout<<"des B()"<<endl;
	}
	int _b;
};
class C:public A
{
	public:
	C()
	{

		cout<<"establish:C()"<<endl;
	}
	~C()
   {
	   cout<<"des C()"<<endl;
	}
	int _c;
};
class D:publicB,public C
{
	public:
	D()
	{

		cout<<"establish:D()"<<endl;
	}
	~D()
   {
	   cout<<"des D()"<<endl;
	}
	int _d;
};
int main()
{
	D d;
	d._a=1;
	d._b=2;
	d._c=3;
	d._d=4;
	cout<<d._a<<endl;
	system("pause");
	return 0;
}
     這個程式在編譯的時候系統會報錯:對A的訪問不明確。

      為什麼會這樣說呢。我們可以看看下圖

      那麼按照這個圖來講的話 我們在從派生類D中呼叫基類A的成員項時,因為派生類D中的成員繼承的是派生類B和C中從基類A中繼承的。所以當我們需要訪問這個成員的時候,我們無法判斷這個繼承下來的成員是從B中還是從C中繼承而來的。編譯器無法判斷便會報錯。應對這樣的問題我們可以使用d.A::_a來訪問這個成員。但是這樣需要在一個類中保留間接共同基類的多份同名成員,不僅佔用較多的儲存空間,還增加了訪問這些成員時的困難,極容易出錯。所以C++提供了虛擬繼承來解決這個問題。只需要在需要繼承的派生類前加virtual.就可以使該繼承成為虛擬繼承。我們將上面的類改造一下。

#include<iostream>
using namespace std;
class A
{
public:
	A()
	{

		cout<<"establish:A()"<<endl;
	}
	~A()
   {
	   cout<<"des A()"<<endl;
	}
	int _a;
};
class B: virtual public A
{
	public:
	B()
	{

		cout<<"establish:B()"<<endl;
	}
	~B()
   {
	   cout<<"des B()"<<endl;
	}
	int _b;
};
class C:  virtual  public A
{
	public:
	C()
	{

		cout<<"establish:C()"<<endl;
	}
	~C()
   {
	   cout<<"des C()"<<endl;
	}
	int _c;
};
class D:public B,public C
{
	public:
	D()
	{

		cout<<"establish:D()"<<endl;
	}
	~D()
   {
	   cout<<"des D()"<<endl;
	}
	int _d;
};
int main()
{
	D d;
	d._a=1;
	d._b=2;
	d._c=3;
	d._d=4;
	cout<<d._a<<endl;
	system("pause");
	return 0;
}
這樣我們就可以方便的訪問我們需要訪問的成員。我們來分析一下虛擬繼承。我們可以先計算一下虛擬繼承使用前後的大小。經過計算,我們測試出在加vitrual時所佔空間為
24個位元組,沒有加virtual時,sizeof出的值是20個位元組。空間增加了四個位元組。我們開啟記憶體檢視一下這多餘的四個位元組是什麼

在這裡我暈了一下 自認為多了兩個指標應該多了八個位元組,其實不然,因為原來記憶體中儲存了兩個成員,但是現在我們只需要儲存一個成員值,這時候獲取值得方法我們下面說,但是此時我們儲存了兩個指標,相對於原來的記憶體空間多了一個指標的空間的大小。剛好四個位元組。

為什麼這裡會儲存兩個指標呢?為了搞清這一點我們直接開啟這裡所指向的空間。


上圖展示了整個派生類的內部空間的情況,從中我們可以很明顯地看出兩個地址指向的內容,第一個地址開啟後顯示了一個數字14也就是十進位制的20,而第二個地址顯示的是一個0c對應十進位制數字的12,可以看到B類的虛指標偏移20個位元組剛好是基類A裡的成員_a;可以看到C類的虛指標偏移12個位元組剛好是基類A裡的成員_a。這樣就解決了我們前面所提到的重複儲存以及派生類的二義性問題。虛擬繼承實際上是儲存了一個地址,地址中儲存了相對於當前地址偏移量,節省了我們整體派生類D的空間。

相關推薦

C++繼承部分總結

    C++是一門面向物件的語言,所以它的大部分操作都與類和方法息息相關,而C++語言也具有三大特性:封裝、繼承和多型。剛好我學習到的內容涉及到了繼承,為了防止遺忘在此進行一下相關的總結。     繼承從字面上的意思來看就是子承父業,繼承首先需要的是一個子類和一個父類。從

繼承部分總結

人工 行為 優先級 類繼承 class pub rgs 靜態方法 修改 一、繼承總結: 1.繼承的概念:繼承是指一個類的定義可以基於另外一個已經存在的類,即子類基於父類,從而實現父類代碼的重用,子類能吸收已有類的數據屬性和行為,並能擴展新的能力。 2.繼承的形式: 【訪問權

C++繼承知識點總結;

C++繼承方式分為公有繼承、私有繼承、保護繼承。關鍵字分別為public 、 private 、 protected。 1.派生類幾種繼承方式總結: 2.基類物件對基類的訪問許可權總結: 3.派生類物件對基類的訪問許可權總結

Online Game Development in C++ 第五部分總結

無限 asa 搜索 清理 屬性 line 而且 驅動力 c函數 教程案例框架描述 該套教程做了一個簡單的汽車控制系統,沒有用到物理模擬。用油門和方向控制汽車的加速度和轉向,同時還有一些空氣阻力和滾動摩擦力的設置增加了真實感。汽車的位置是通過加速度和時間等計算出來的。 關鍵的

關於c++繼承的一系列問題總結

面向物件設計的重要目的之一就是程式碼重用,而繼承和多型是面向物件的兩個最主要的特徵。本文將主要圍繞c++中的繼承展開討論,並以一些簡單的例子來說明。 一.首先觀察在繼承中,成員物件的初始化。 1.建立派生類物件時,程式首先建立基類物件,這意味著基類物件應當在程式進入派生類

C語言部分知識總結

目前已初步學習C語言前五章,以下是對知識點的概括以及個人的理解。 基礎知識: 1.   一個C語言源程式可以由一個或多個原始檔組成。 2.   每個原始檔可由一個或多個函式組成。 3.   一個源程式不論由多少個檔案組成,都有一個且只能有一個mai

C++繼承相關知識點總結

1:派生類繼承基類的成員並且可以定義自己的附加成員。每個派生類物件包含兩個部分:從基類繼承的成員和自己定義的成員。         每個派生類物件都有基類部分,包括基類的private成員。類可以訪

C++ 作業系統 部分筆試選擇題總結

1.假如在一個函式中有一下程式碼() void fun() {   int * p = (int*)malloc(sizeof(int));  int * q = p; } A. p指向的空間在堆上,q指向的空間在棧上 B. p指向的空間在棧上,q指向的空間在堆上 C. p

C++:繼承總結

繼承的相關概念 繼承是面向物件複用的重要手段。繼承是型別之間的關係建模,通過繼承類的關係,可以達到複用的目的。比如下面這個例子: 老師,學生,保安都可以由人這個類繼承下來。 實現一個簡單的類 繼承是一種複用手段,在繼承關係裡父類的成員都會變成子

關於c++中的virtual繼承總結

1.在什麼時候需要虛擬繼承呢? 虛擬繼承是多重繼承特有的概念。虛擬基類是為了解決多重繼承而出現的。例如A繼承了Y,Z。而Y,Z繼承自X,因此A兩次出現了類x中的變數和函式,可以將Y,Z定義為虛擬繼承。而x則變成了虛擬繼承的基類 class X{ }; class Y: p

C語言資料型別部分總結

第三章  資料和C 1、float型別的資料是遵循IEEE754標準在計算機內部進行儲存的,這樣帶來的問題是儲存的時候可能會丟失進度,例如: # include<stdio.h> int main(void) {      float value = 66.6

Inside the C++ Object Model 總結

數據 truct access 連續 bject ase data 地址 del 1.C++ 虛函數的實現是為class安插一個vptr指向一個數組。數組第一項通常保存type_info。其他項為虛函數地址。或許還存在指向virtual base class的指針。 2.通

C++ 繼承(一)

c++ 繼承(一)C++繼承的標準寫法:class BaseMonster { public: string name; string skill; uint32_t hp; void attack() { cout << "Base ::

C#設計模式總結

替代 version 論文 詞典 log 調用接口 常用 私有構造函數 額外 原文地址:http://www.cnblogs.com/zhili/p/SingletonPatterm.html 一、引言   經過這段時間對設計模式的學習,自己的感觸還是很多的,因為我現在在

C++技術問題總結-第8篇 STL內存池是怎麽實現的

lists ng- 碎片 -m heap 策略 自管理 blog watermark STL內存池機制,使用雙層級配置器。第一級採用malloc、free,第二級視情況採用不同策略。這樣的機制從heap中要空間,能夠解決內存碎片問題。 1.內存申請流程圖

C++繼承

.com adding http line 情況 狀態 其他 pri width 轉自:http://www.cnblogs.com/qlwy/archive/2011/08/25/2153584.html 公有繼承(public)、私有繼承(private)、保護繼承

C++繼承:公有,私有,保護(轉)

情況 pre mes 派生類 它的 保持 ++ col ble 公有繼承(public)、私有繼承(private)、保護繼承(protected)是常用的三種繼承方式。 1. 公有繼承(public) 公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持

2014 Unity3d大會的部分總結

打開 family -s roc bject 釋放 tag 四大 enc 一、項目開發。管理和公布策略 1. 四大準則 a. 美術的資源量 b. 美術規範,要依據開發什麽樣的遊戲制定統一的規範,這樣盡可能的形成統一的規範。然後

C++繼承與派生

規則 方法 三種 賦值兼容 順序 spa 構造 指針 rtu 2017-06-25 23:00:59 c++中的繼承和派生是面向對象編程中的一個重要內容,通過繼承可以實現代碼的復用,同時繼承也是實現多態性的基礎。 一、c++繼承的基本形式 class 派生類名:繼承方式 基

C++繼承意義梳理

bsp order 集成 對象 protect tab 記錄 不可見 基類 C++的繼承屬性理解和記錄 常用的繼承方式:{公有繼承} 、 {保護繼承} 、 {私有繼承} Public P