C++建構函式
當建立一個對象時,往往需要做一些初始化工作,例如對資料成員賦值等。為瞭解決這個問題,C++提供了建構函式。
建構函式(Constructor)是一種特殊的成員函數,它的名字和類名相同,沒有傳回值,不需要使用者調用(使用者也不能調用),而是在建立對象時自動執行。建構函式的作用是在建立對象時進行初始化工作,最常見的就是對成員變數賦值。
一個建構函式的例子:
#include <iostream>using namespace std;class Student{private: char *name; int age; float score;public: //聲明建構函式 Student(char *, int, float); //聲明普通成員函數 void say();};//定義建構函式Student::Student(char *name1, int age1, float score1){ name = name1; age = age1; score = score1;}//定義普通成員函數void Student::say(){ cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<endl;}int main(){ //根據建構函式建立對象 Student stu("小明", 15, 90.5f); //傳參形式類似於函數調用 stu.say(); return 0;}
運行結果:
在類中我們定義了一個建構函式 Student(),它的作用是給3個 private 屬性的成員變數賦值。在 main 函數中,我們根據建構函式建立了一個對象 stu;因為建構函式有參數,所以建立對象時要相應地傳入實參,形式類似於函數調用。
讀者要注意:一旦在類中定義了建構函式,那麼建立對象時一定會被執行;如果建構函式有參數,建立對象時就要傳參。
另外,建構函式主要用來進行初始化,沒有傳回值(有傳回值沒有任何意義),這就意味著:
不管是聲明還是定義,函數名前面都不能出現傳回值類型,即使是 void 也不允許;
函數體中不能有 return 語句。
預設建構函式
如果使用者自己沒有定義建構函式,那麼編譯器會自動產生一個預設的建構函式,只是這個建構函式的函數體是空的,也沒有參數,不執行任何操作。比如上面的 Student 類,預設產生的建構函式如下:
一個類,必須有建構函式,要麼使用者自己定義,要麼編譯器自動產生。一旦使用者自己定義了建構函式,不管它是 public 屬性的,還是 private、protected 屬性的,編譯器都不再自動產生。上面的 Student 類,只有一個建構函式,就是我們自己定義的。
實際上,編譯器只有在必要的時候才會產生預設建構函式,而且它的函數體一般不為空白。預設建構函式的目的是協助編譯器做初始化工作,而不是協助程式員。這是C++的內部實現機制,這裡不再深究,初學者可以按照上面說的“一定有一個空函數體的預設建構函式”來理解。
建構函式的重載
和普通成員函數一樣,建構函式是允許重載的。一個類可以提供多個建構函式,讓使用者在建立對象時進行選擇,編譯器會根據建立對象時傳遞的參數來確定調用哪一個建構函式。也就是說:
只有一個建構函式會被執行;
建立對象時提供的參數必須和其中的一個建構函式匹配,否則編譯錯誤。
一個建構函式重載的例子:
#include <iostream>using namespace std;class Student{private: char *name; int age; float score;public: //聲明建構函式 Student(); Student(char *, int, float); //聲明普通成員函數 void setname(char *); void setage(int); void setscore(float); void say();};//定義建構函式Student::Student(){}Student::Student(char *name1, int age1, float score1){ name = name1; age = age1; score = score1;}//定義普通成員函數void Student::setname(char *name1){ name = name1;}void Student::setage(int age1){ age = age1;}void Student::setscore(float score1){ score = score1;}void Student::say(){ cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<endl;}int main(){ //建立對象時初始化成員變數 Student stu1("小明", 15, 90.5f); stu1.say(); //調用成員函數來初始化成員變數的值 Student stu2; stu2.setname("李磊"); stu2.setage(16); stu2.setscore(95); stu2.say(); return 0;}
運行結果:
小明的年齡是 15,成績是 90.5李磊的年齡是 16,成績是 95
類中定義了兩個建構函式,一個帶參數一個不帶參數,它們是重載關係。當根據不帶參數的建構函式建立對象時,不需要傳參,成員變數不會被初始化,所以要調用成員函數來設定它們的值。
C++帶參數的建構函式
不帶參數的建構函式使該類的每一個對象都得到相同的初始值。
如果希望對不同的對象賦予不同的初始值,則需要使用帶參數的建構函式,在調用不同對象的建構函式時,將不同的資料傳給建構函式,以實現不同的初始化。
建構函式首部的一般格式為:
建構函式名(類型1 形參1, 類型2 形參2, …)
由於使用者是不能調用建構函式的,因此無法採用常規的調用函數的方法給出實參。實參是在建立對象時給出的。建立對象的一般格式為:
【例】有兩個長方柱,其長、寬、高分別為12, 20, 25和10, 14, 20,求它們的體積。編寫一個基於對象的程式,在類中用帶參數的建構函式。
#include <iostream>using namespace std;class Box{ public : Box(int,int,int); int volume( ); private : int height; int width; int length;};//聲明帶參數的建構函式//聲明計算體積的函數Box::Box(int h,int w,int len) //在類外定義帶參數的建構函式{ height=h; width=w; length=len;}int Box::volume( ) //定義計算體積的函數{ return (height*width*length);}int main( ){ Box box1(12,25,30); //建立對象box1,並指定box1長、寬、高的值 cout<<"The volume of box1 is "<<box1.volume( )<<endl; Box box2(15,30,21); //建立對象box2,並指定box2長、寬、高的值 cout<<"The volume of box2 is "<<box2.volume( )<<endl; return 0;}
程式運行結果如下:
The volume of box1 is 9000The volume of box2 is 9450
可以知道:
帶參數的建構函式中的形參,其對應的實參在定義對象時給定。
用這種方法可以方便地實現對不同的對象進行不同的初始化。
用參數初始化表對資料成員初始化
上面介紹的是在建構函式的函數體內通過指派陳述式對資料成員實現初始化。C++還提供另一種初始化資料成員的方法——參數初始化表來實現對資料成員的初始化。這種方法不在函數體內對資料成員初始化,而是在函數首部實現。
例中定義建構函式可以改用以下形式:
Box::Box(int h,int w,int len):height(h),width(w), length(len){ }
這種寫法方便、簡練,尤其當需要初始化的資料成員較多時更顯其優越性。甚至可以直接在類體中(而不是在類外)定義建構函式。