C 學習之路 繼承與派生(四)拓展與總結

2022-03-06 03:13:55 字數 3828 閱讀 4779

本節主要由兩部分內容組成,分別是(1)基類與派生類的轉換和(2)繼承與組合

在前幾篇部落格中,可以看到在3種繼承方式中,只有公有繼承能較好的保留基類的特徵,它保留了除建構函式和析構函式以外所有的基類成員,基類的公有或保護成員的訪問許可權在派生類中全部保留了下來,在派生類外可以呼叫基類的公有成員函式以訪問基類的私有成員。而非公用派生類不能實現基類的全部功能(例如在派生類外不能呼叫基類的公有成員函式訪問基類的私有成員)。因此,只有公有派生類才是基類真正的子型別,它完整的繼承了基類的功能。

如果不同型別資料之間可以自動轉換和賦值,成為賦值相容。基類與公用派生類之間具有賦值相容關係,由於派生類中包含從基類繼承的成員,因此可以將派生類的值賦給基類物件,在用到基類物件的時候可以用其子物件代替。基類與(公用)派生類之間的賦值相容表現在以下4個方面:

(1)派生類物件可以向基類物件賦值。若類b是類a的公用派生類,則可進行以下操作:

2: b b1 ;     //定義公用派生類b的物件b1
3: a1 = b1 ;   //用派生類b的物件b1對基類物件a1賦值
在進行賦值時捨棄派生類自己新增加的成員,所謂賦值只是對資料成員賦值,對成員函式不存在賦值問題。賦值後不能試圖通過物件a1去訪問派生類物件b1新增的成員,假設age是派生類b中新增加的成員,則:

2: b1.age = 21 ;   //正確,b1中包含派生類中增加的成員
總結:只能用子類物件對基類物件賦值,而不能用基類物件對其子類物件賦值。同一基類的不同派生類物件之間也不能賦值。

(2)派生類物件可以代替基類物件向基類物件的引用進行賦值或初始化。

如果已經定義了基類a物件a1,可以定義a1的引用變數:

2: b b1 ;         //定義公用派生類b物件b1
3: a& r = a1 ;    //定義基類a物件的引用r,並用a1對其初始化
4:

//也可以將上面最後一行改為

5: a& r  = b1 ;    //定義基類a物件的引用r,並用派生類b物件b1對其初始化
此時r並不是b1的別名,也不是與b1共享同一段儲存單元。它只是b1中基類部分的別名,r與b1中基類部分共享同一段儲存單元,r與b1具有相同的起始位址。

(3)如果函式的引數是基類物件或基類物件的引用,相應的實參可以用子類物件。例如:

void fun ( a& r ) //形參是a類物件的引用

2:
5: b b1 ;                           //定義公用派生類的物件b1
6: fun( b1 ) ;                      //輸出b物件b1的基類的資料成員num的值
在fun函式中只能輸出派生類中基類成員的值。

(4)派生類物件的位址可以賦給指向基類物件的指標變數,即指向基類物件的指標變數可以指向派生類物件。示例程式如下:

#include

2:

#include

3:

using

namespace std ;

4:
5:

class student //宣告student類

6: ;
15: student::student( int n , string nam , float s )  //定義建構函式
16:
21:

void student::display() //定義輸出函式

22:
27:
28:

class graduate : public student //宣告公用派生類graduate

29: ;
36: graduate::graduate( int n , string nam , float s , float w ) : stident( n , nam , s )
37:                     , wage( w ) {}            //定義建構函式
38:

void graduate::display() //定義輸出函式

39:
43:
44:

int main()

45:
先看一下程式執行結果,再進行具體的分析:

分析:有很多讀者會認為,在派生類中有兩個同名的display成員函式,根據同名覆蓋的規則,第二次被呼叫的應當是派生類graduate物件的display函式,在執行graduate::display函式過程中呼叫student::display函式,輸出num,name,score,然後再輸出wage的值。很明顯與上述結果不符,why?問題在於pt是指向student類物件的指標變數,即使讓它指向了grad1,但實際上pt指向的是從grad1從基類繼承的部分。通過指向基類物件的指標,只能訪問派生類中的基類成員,而不能訪問派生類增加的成員。

通過本例可以看到,用指向基類物件的指標變數指向子類物件是合法的、安全的,不會出現編譯上的錯誤。但人們更希望通過使用基類指標能夠呼叫基類和子類物件的成員,要解決這個問題,就要用到以後會講到的多型性和虛函式。

在乙個類中以另乙個類的物件作為資料成員的,稱為類的組合。例如,宣告professor類是teacher類的派生類,另有乙個類birthdate,包含year,month,day等資料成員。我們可以將教授生日的資訊加入到professor類的宣告中。如:

class teacher //宣告教師類

2: ;
10:
11:

class birthdate //宣告生日類

12: ;
20:
21:

class professor : public teacher //宣告教授類

22: ;
類的組合和類的繼承一樣,都是有效地利用已有類的資源。但二者的概念和用法不同。通過繼承建立了派生類與基類的關係,這是一種「is-a」的關係,如「白貓是貓」,派生類是基類的具體化的實現,是基類的一種。通過組合則建立了成員類和組合類的關係,它們之間是「has-a」的關係。不能說professor是乙個birthdate,只能說教授有乙個birthdate的屬性。

縮短軟體開發過程的關鍵是鼓勵軟體的重用。繼承機制在很大一部分上解決了這個問題。編寫物件導向的程式時要把注意力放在實現對自己有用的類上面,對已有的類加以整理和分類,進行剪裁和修改,並在此基礎上集中精力編寫派生類新增加的部分。

人們為什麼這麼看重繼承,要求在軟體開發中使用繼承機制,盡可能的通過繼承建立一批新的類,有以下幾個原因:

(1)有許多基類是被程式的其他部分或其他程式使用的,這些程式要求保持原有的基類不受破壞。

(2)使用者往往得不到基類的源**。如果使用的類庫,使用者無法知道成員函式的**,因此也就無法對基類進行修改,保證了基類的安全。

(3)在類庫中,乙個基類可能已被指定與使用者所需的多種組建有聯絡,因此類庫不允許被修改。

(4)許多類是專門被設計為基類的,並沒有什麼獨立的功能,只是乙個框架,或者說是抽象類。設計這些通用的類目的是建立通用的資料結構,以便使用者在此基礎上新增各種功能建立派生類。

C 繼承與派生 學習筆記

一 繼承和派生的基本概念 繼承是c 語言中的一種重要的機制,也是物件導向的乙個重要特徵,實現了物件導向程式設計思想中軟體復用的功能。繼承的實質就是通過現有的類的特徵,構造乙個具有現有類特徵的新類,這個新類成為派生類。派生類是從乙個或者多個以前定義的類 基類 繼承資料和函式,同時增加或者重定義資料和函...

C 繼承與派生 學習筆記

一 繼承和派生的基本概念 繼承是c 語言中的一種重要的機制,也是物件導向的乙個重要特徵,實現了物件導向程式設計思想中軟體復用的功能。繼承的實質就是通過現有的類的特徵,構造乙個具有現有類特徵的新類,這個新類成為派生類。派生類是從乙個或者多個以前定義的類 基類 繼承資料和函式,同時增加或者重定義資料和函...

C 繼承與派生

派生新類 吸收已有類的成員 調整已有類成員和新增新的成員 class 派生類名 繼承方式 基類名1,繼承方式 基類名2,派生類成員宣告 繼承方式有 public protected private 預設 公有繼承 基類的公有和保護乘員的訪問屬性在派生類中不變,基類的私有成員不能直接訪問 型別相容規則...