C++_派生類的建構函式及派生類和基類之間的特殊關係
派生類和基類的概念及派生類建構函式的原理:
建立一個叫做TableTennisPlayer的基類,記錄會員的名字和是否有球桌。
1 //宣告一個基類 2 class TableTennisPlayer 3 { 4 private: 5 string firstname; 6 string lastname; 7 bool hasTable; 8 9 public: 10 TableTennisPlayer(); 11 void Name() const; 12 bool HasTable() const {return hasTable;};13 void ResetTable(bool v) {hasTable =v;}; 14 } 15 16 //建構函式的定義 17 TableTennisPlayer::TableTennisPlayer(const string & fn,const string & ln ,bool ht):firstname(fn),lastname(ln),hasTable(ht) {} 18 19 //Name函式的定義 20 void TableTennisPlayer::Name() cosnt 21 { 22 std::cout << lastname << ", "<<firstname; 23 }
接下來宣告一個派生類:
1 //宣告一個派生類,多了個排名的資料成員 2 class RatedPlayer: public TableTennisPlayer 3 { 4 private: 5 unsigned int rating; 6 public: 7 RatedPlayer(unsigned int r =0,const string & fn = "none",const string & ln ="none", bool ht = false); 8 RatedPlayer(unsigned r, constTableTennisPlayer & tp); 9 unsigned int Rating() const {return rating;} 10 void ResetRating (unsigned int r) {rating = r;} 11 }; 12 13 //建構函式的實現 14 RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht) 15 { 16 rating =r; 17 } 18 19 RatedPlayer(unsigned r, const TableTennisPlayer & tp):TableTennisPlayer(tp),rating(r) 20 { 21 }
派生類不能訪問基類的私有成員,而必須通過基類方法進行訪問。
因此派生類建構函式必須使用基類建構函式;
建立派生類物件時,程式首先建立基類物件。從概念上講,這意味著基類物件應當在程式進入派生類建構函式之前被建立。->C++使用成員初始化列表來完成這種工作。
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht)
{
rating = r;
}
如果省略了基類建構函式的話:
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht)
{
rating = r;
}
首先還是建立基類物件,如果不呼叫基類建構函式,程式將使用預設的基類建構函式。
因此上述程式碼與下面等效:
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer()
{
rating = r;
}
除非要使用預設建構函式,否則應該顯式呼叫正確的基類建構函式。
還有一種建構函式程式碼:
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):TableTennisPlayer(tp)
{
rating = r;
}
這裡也是將TableTennisPlayer的資訊傳遞給了TableTennisPlayer建構函式。
這種方式將呼叫基類的複製建構函式,如果基類沒有定義複製建構函式,但又要使用它,則編譯器將自動生成一個。
甚至還可以對派生類成員使用成員初始化列表語法:
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):TableTennisPlayer(tp),rating(r)
{
}
派生類建構函式的要點總結:
首先建立基類物件;
派生類建構函式應通過成員初始化列表(TableTennisPlayer(tp))將基類資訊傳遞給基類建構函式;
派生類建構函式應初始化派生類新增的資料成員(rating = r)。
如果沒有提供顯示建構函式,將使用隱式建構函式。
理解派生類物件建立過程的物件建立順序:先建立基類物件,在建立派生類物件;
釋放物件的順序與建立物件的順序相反:首先執行派生類的解構函式,然後自動呼叫基類的解構函式。
成員初始化列表:(TableTennisPlayer(tp))
派生類建構函式可以使用初始化器列表機制將值傳遞給基類建構函式。
derived :: derived(type1 x, type2 y) : base(x,y)
{
...
}
Derived是派生類,base是基類。X和Y是基類建構函式使用的變數。
如果派生類建構函式接收到引數10和12,這種機制將把10和12傳遞給定義為接受這些型別的引數的基類建構函式。類只能將值傳遞迴相鄰的基類。虛基類除外,虛基類可以使用相同的機制將資訊傳遞給相鄰的基類,以此類推。如果沒有在成員初始化列表中提供基類建構函式,程式將使用預設的積累建構函式。成員初始化列表只能用於建構函式。
派生類和基類的關係:(引用、指標)
派生類可以使用基類的方法,條件是方法不是私有的。
基類指標可以再不進行顯式型別轉換的情況下指向派生類物件。
基類引用可以再不進行顯式型別轉換的情況下引用派生類物件。
但是基類指標和引用只能呼叫基類的方法。
C++中要求引用和指標型別與賦予的型別匹配,但這一規則對繼承來說是例外。這例外是單向的,也就是說不能將基類物件和地址賦給派生類引用和指標。
但是這種關係是單向的,不能將基類物件和地址賦給派生類引用和指標。
這樣要求是有道理的:如果允許基類引用隱式地呼叫派生類方法,則可以使用基類引用為派生類物件呼叫基類的方法。因為派生類繼承了基類的方法,所以這樣做不會有問題。
如果可以將基類物件賦給派生類引用,將發生什麼情況?派生類引用能夠為基類物件呼叫派生類方法,這是沒有意義的。例如TableTennisPlayer沒有rating成員。
用一個圖總結如下:
基類引用和指標可以指向派生類物件,將出現一些很有意思的現象:
第一個例子
Show函式如下:
1 void Show(const TableTennisPlayer & rt) 2 { 3 using std::cout; 4 cout << "Name:"; 5 rt.name(); 6 cout << "\nTable:"; 7 if(rt.HasTable()) 8 cout<<"yes\n"; 9 else 10 cout <<"no\n"; 11 }
引數rt是一個基類引用,它可以指向基類物件或派生類物件,所以可以在Show()中使用TableTennisPlayer引數或Ratedplayer引數。
1 TableTennisPlayer player1("Tara","Boomdea",false); 2 RatedPlayer rplayer1(1110,"Mallory","Duck",true); 3 Show(player1); 4 Show(rplayer1);
對於形參為指向基類的指標的函式,也存在相似的關係。
總結來說:形參是指向基類引用的函式,可以傳基類實參,也可以傳派生類實參。
第二個例子(派生類物件可以對基類物件進行初始化)
RatedPlayer olaf1(1840,"Olaf","Loaf",true);
TabelTennisPlayer olaf2(olaf1);
第二行如何初始化?類定義中隱式複製建構函式:
TableTennisPlayer(const TableTennisPlayer &);
這個建構函式的形參是基類引用。因此它可以指向派生類物件。也就是說要將olaf2初始化為olaf1時,將要呼叫該建構函式,它複製了olaf1的firstname、lastname和hasTable成員。換句話說,它將olaf2初始化為巢狀在RatedPlayer物件olaf1中的TableTennisPlayer物件。
還可以將派生物件賦值給基類物件
RatedPlayer olaf1(1840,"Olaf","Loaf",true);
TableTennisPlayer winner;
winner = olaf1;
這種情況下程式將使用隱式過載賦值運算子;
TableTennisPlayer & operator=(cosnt TableTennisPlayer &) const;
可以看出基類引用指向的也是派生類物件,因此可以將olaf1的基類部分複製給winner。