C++基礎(5)——類繼承

1. 從一個類派生出另一個類時,原始類稱爲基類,繼承類稱爲派生類。

class TableTennisPlayer//一個簡單的基類
{
 private:
 	string firstname;
	string lastname;
	bool hasTable;
public:
	TableTennisPlayer(const string & fn = “none”,const string & ln = “none”,bool ht = false);
        void Name() const;
        bool HasTable() const {return hasTable;};
        void ResetTable(bool v){hastable = v;};
};
//將RatedPlayer類聲明爲從TableTennisPlayer類派生而來
class RatedPlayer : public TableTennisPlayer
{
};
//冒號指出RatedPlayer類的基類是TableTennisPlayer類。
  • RatedPlayer對象將具有以下特徵:
    • 派生類對象存儲了基類的數據成員;
    • 派生類對象可以使用基類的方法;
  • 上述特殊的聲明頭表明TableTennisPlayer是一個公有基類,這被稱爲公有派生。派生類對象包含基類對象。使用公有派生,基類的公有成員將成爲派生類的公有成員;基類的私有部分也將成爲派生類的一部分,但只能通過基類的公有和保護方法訪問。

2. 需要在繼承特性中添加

  • 派生類需要自己的構造函數
  • 派生類可以根據需要添加額外的數據成員和成員函數
class RatedPlayer : public TableTennisPlayer
{
private:
	unsigned int rating;//增加一個數據成員
public:
	RatedPlayer(unsigned int r=0,const string & fn =“none”, const string & ln = “none”, bool ht = false);
	RatedPlayer(unsigned int r, const TableTennisPlayer &tp);
	unsigned int Rating() const {return rating;};
	void ResetRating(unsigned int r){rating = r;};
};
  • 構造函數必須給新成員(if have)和繼承的成員提供數據。在第一個RatedPlayer構造函數中,每個成員對應一個形參;而第二個RatedPlayer構造函數使用一個TableTennisPalyer參數,該參數包括firstname、lastname和hasTable。

3. 構造函數:訪問權限的考慮

  • 派生類不能直接訪問基類的私有成員,而必須通過基類方法進行訪問。派生類構造函數必須使用基類構造函數。
  • 創建派生類對象時,程序首先創建基類對象。從概念上說,這意味着基類對象應當在程序進入派生類構造函數之前被創建。
  • 第一個RatedPlayer構造函數的代碼
RatedPlayer::RatedPlayer(unsigned int r,const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht)
{
rating = r;
}
  • 第二個RatedPlayer構造函數的代碼
RatedPlayer::RatedPlayer(unsigned int r,const TableTennisPlayer & tp)
:TableTennisPlayer(tp)
{
rating = r;
}
  • 派生類構造函數的要點
    • 首先創建基類對象;
    • 派生類構造函數應通過成員初始化列表將基類信息傳遞給基類構造函數;
    • 派生類構造函數應初始化派生類新增的數據成員。

4. 繼承:is-a關係

  • C++有3種繼承方式:公有繼承、保護繼承和私有繼承。
  • 公有繼承建立的關係稱爲is-a-kind-of(是一種)關係,通常用術語is-a,即派生類對象也是一個基類。
  • 公有繼承不建立has-a關係,不能建立is-like-a關係,不建立is-implemented-as-a(作爲……來實現)關係,不建立uses-a關係。

5. 多態公有繼承

  • 有兩種重要的機制可用於實現多態公有繼承:在派生類中重新定義基類的方法;使用虛方法。
  • 非構造函數不能使用成員初始化列表語法,但派生類方法可以調用公有的基類方法。
void BrassPlus::ViewAcct() const
{
…
	Brass::ViewAcct();
	cout<<”Maxmum loan : $”<<maxloan<<endl;
	cout<<”Owed to bank :	$”<<owesBank<<endl;
	cout<<”Loan Rate:”<<100*rate<<”%\n”;
…
}
/*
BrassPlus:: ViewAcct()顯示新增的BrassPlus數據成員,
並調用基類的Brass::ViewAcct()來顯示基類數據成員。
在派生類方法中,標準技術是使用作用域解析運算符來調用基類方法。
*/

6. 靜態聯編和動態聯編

  • 將源代碼中的函數調用解釋爲執行特定的函數代碼塊被稱爲函數名聯編(binding)。在編譯過程中進行聯編被稱爲靜態聯編(static binding)。編譯器必須生成能夠在程序運行時選擇正確的虛方法的代碼,這被稱爲動態聯編(dynamic binding)。
  • 將派生類引用或指針轉換爲基類引用或指針被稱爲向上強制轉換(upcasting),這時公有繼承不需要進行顯式類型轉換。將基類指針或引用轉換爲派生類指針或引用稱爲向下強制轉換(downcasting),如果不使用顯式類型轉換,則向下強制轉換是不被允許的。

7. 虛函數

  • 在基類方法的聲明中使用關鍵字virtual可使該方法在基類以及所有的派生類(包括從派生類派生出來的類)中是虛的。
  • 如果要在派生類中重新定義基類的方法,則將它設置爲虛方法;否則,設置爲非虛方法。
  • 構造函數不能是虛函數。創建派生類對象時,將調用派生類的構造函數,而不是基類的構造函數,然後,派生類的構造函數將使用基類的一個構造函數,這種順序不同於繼承機制。派生類不繼承基類的構造函數,將類構造函數聲明爲虛的沒什麼意義。
  • 析構函數應當是虛函數,除非類不用做基類。

8. 訪問控制:protected

  • private和protected之間的區別只有在基類派生的類中才會表現出來,派生類的成員可以直接訪問基類的保護成員,但不能直接訪問基類的私有成員。
  • 最好對類數據成員採用私有訪問控制,不要使用保護訪問控制;同時通過基類方法使派生類能夠訪問基類數據。
  • 然而,對於成員函數來說,保護訪問控制很有用,它讓派生類能夠訪問公衆不能使用的內部函數。

9. 編譯器生成的成員函數

  • 默認構造函數。默認構造函數要麼沒有參數,要麼所有的參數都有默認值,提供構造函數的動機之一是確保對象總能被正確的初始化,因此,最好提供一個顯式默認構造函數,將所有的類數據成員都初始化爲合理的值。
  • 複製構造函數接受其所屬類的對象作爲參數。在下述四個情況下,將使用複製構造函數:將新對象初始化爲一個同類對象;按值將對象傳遞給函數;函數按值返回對象;編譯器生成臨時對象。
  • 默認的賦值運算符用於處理同類對象之間的賦值;如果語句創建新的對象,則使用初始化;如果語句修改已有對象的值,則是賦值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章