3.6 C++繼承機制下的構造函數
參考:http://www.weixueyuan.net/view/6363.html
總結:
在codingbook類中新增了一個language成員變量,為此必須重新設計新的構造函數。在本例中book類中有一個默認構造函數和一個帶參數的構造函數,codingbook類中同樣聲明了兩個構造函數,一個默認構造函數和一個帶參數的構造函數,默認構造函數顯式調用基類的默認構造函數,帶參構造函數顯式調用基類的帶參構造函數。
codingbook():book(){lang = none;} //定義派生類的構造函數時 顯示調用 基類的構造函數
codingbook::codingbook(language lang, char * t, double p):book(t,p)
如果繼承關系有好幾層的話,例如A類派生出B類,B類派生出C類,則創建C類對象時,構造函數的執行順序則為A的構造函數,其次是B的構造函數,最後是C類的構造函數。構造函數的調用順序是按照繼承的層次,自頂向下,從基類再到派生類的。
在前一章節中,我們介紹了構造函數的功能和用法,派生類同樣有構造函數。當我們創建一個派生類對象的時候,基類構造函數將會被自動調用,用於初始化派生類從基類中繼承過來的成員變量。而派生類中新增的成員變量則需要重新定義構造函數用於初始化了。
例1:
#include<iostream> using namespace std; class book {public: book(); book(char* a, double p = 5.0); void setprice(double a); double getprice()const; void settitle(char* a); char * gettitle()const; void display(); private: double price; char * title; }; class book_derived :public book { public: void display(); }; book::book(char* a, double p) { title = a; price = p; } book::book() { title = "NoTitle"; price = 0.0; } void book::setprice(double a) { price = a; } double book::getprice()const { return price; } void book::settitle(char* a) { title = a; } char * book::gettitle()const { return title; } void book::display() { cout<<"The price of "<<title<<" is $"<<price<<endl; } void book_derived::display() { cout<<"The price of "<<gettitle()<<" is $"<<getprice()<<endl; } int main() { book_derived b; b.display(); return 0; }
在本例中定義了book_derived類,該類沒有自身的成員變量,類中所有成員變量都繼承自book類,類中成員函數僅有一個display函數,該函數遮蔽了基類book中的display函數。在主函數中定義派生類的對象b,之後調用派生類的display函數,程序運行結果為:“The price of NoTitle is $0”。
從這例1中,我們不難看出派生類在創建對象時會自動調用基類構造函數。如果像例1這種情況,派生類中沒有新增成員變量,基類的構造函數功能已經滿足派生類創建對象初始化需要,則派生類則無需重新自定義一個構造函數,直接調用基類構造函數即可。如果派生類中新增了成員變量,這時如果需要在創建對象時就進行初始化則需要自己設計一個構造函數,具體見例2。
例2:
#include<iostream> using namespace std; enum language{none, cpp, java, python, javascript, php, ruby}; class book { public: book(); book(char* a, double p = 5.0); void setprice(double a); double getprice()const; void settitle(char* a); char * gettitle()const; void display(); private: double price; char * title; }; class codingbook: public book { public : codingbook():book(){lang = none;} codingbook(language lang, char * t, double p); void setlang(language lang); language getlang(){return lang;} void display(); private: language lang; }; book::book(char* a, double p) { title = a; price = p; } book::book() { title = "NoTitle"; price = 0.0; } void book::setprice(double a) { price = a; } double book::getprice()const { return price; } void book::settitle(char* a) { title = a; } char * book::gettitle()const { return title; } void book::display() { cout<<"The price of "<<title<<" is $"<<price<<endl; } void codingbook::setlang(language lang) { this->lang = lang; } codingbook::codingbook(language lang, char * t, double p):book(t,p) { this->lang = lang; } void codingbook::display() { book::display(); cout<<"The language is "<<lang<<endl; } int main() { codingbook cpp; cpp.display(); codingbook java(java, "Thinking in Java", 59.9); java.display(); return 0; }
本例中定義了兩個類book類和codingbook類,codingbook類是book類的派生類。在codingbook類中新增了一個language成員變量,為此必須重新設計新的構造函數。在本例中book類中有一個默認構造函數和一個帶參數的構造函數,codingbook類中同樣聲明了兩個構造函數,一個默認構造函數和一個帶參數的構造函數,默認構造函數顯式調用基類的默認構造函數,帶參構造函數顯式調用基類的帶參構造函數。在主函數中定義了codingbook類的對象cpp,該對象調用codingbook類的默認構造函數,codingbook類中的默認構造函數先會調用基類的默認構造函數將title和price進行初始化,之後才會執行自身函數體中的內容。之後又定義了codingbook類對象java,該對象在定義時後面接有三個參數,很明顯是需要調用codingbook類的帶參構造函數,其中java參數用於初始化lang成員變量,而後兩個參數則用於初始化從基類繼承過來的title和price兩個成員變量,當然初始化順序依然是先調用基類的帶參構造函數初始化title和price,然後再執行自身函數體中的初始化代碼初始化lang成員變量。
最後程序運行結果如下:
The price of NoTitle is $0
The language is 0
The price of Thinking in Java is $59.9
The language is 2
在這個例子中language沒有顯示為java或者cpp,只顯示為0和2,這個熟悉枚舉類型的應該都清楚,枚舉類型在本例中其實就是從0開始的int類型。
從例2中,我們可以很清楚的看到,當我們創建派生類對象時,先由派生類構造函數調用基類構造函數,然後再執行派生類構造函數函數體中的內容,也就是說先執行基類構造函數,然後再去執行派生類構造函數。如果繼承關系有好幾層的話,例如A類派生出B類,B類派生出C類,則創建C類對象時,構造函數的執行順序則為A的構造函數,其次是B的構造函數,最後是C類的構造函數。構造函數的調用順序是按照繼承的層次,自頂向下,從基類再到派生類的。
例3:
#include<iostream> using namespace std; class base { public: base(){x = 0; y = 0; cout<<"base default constructor"<<endl;} base(int a, int b){x = a; y = b; cout<<"base constructor"<<endl;} private: int x; int y; }; class derived: public base { public: derived():base(){z = 0; cout<<"derived default constructor"<<endl;} derived(int a, int b, int c):base(a,b){z = c; cout<<"derived constructor"<<endl;} private: int z; }; int main() { derived A; derived B(1,2,3); return 0; }
本例中定義了兩個類,基類base中定義了一個默認構造函數和一個帶參數的構造函數。派生類derived中同樣定義了兩個構造函數,這兩個構造函數一個為默認構造函數,一個為帶參構造函數。派生類中的默認構造函數顯式調用基類默認構造函數,帶參構造函數顯式調用基類的帶參構造函數。我們在主函數中定義了派生類的兩個對象,這兩個對象一個是調用派生類的默認構造函數,另一個調用派生類的帶參構造函數。
這個程序運行結果如下:
base default constructor
derived default constructor
base constructor
derived constructor
從運行結果可以看出創建對象時先是執行基類的構造函數,然後再是執行拍攝呢類構造函數。構造函數執行順序是按照繼承順序自頂向下執行。
3.6 C++繼承機制下的構造函數