構造函數
當定義了一個整型變量:
int a;
這會申請了一塊內存空間來存儲a,但是這塊內存中原本有數據的,可能是任何值,這不是你所希望的,若你就希望a表示1,所以要把a的值賦值為1。
int a = 1;
例:
#include <iostream> using namespace std; class Date { int d, m, y; public: void init(int dd, int mm, int yy) { d = dd; m = mm; y = yy; }; void add_year(int n) { y+=n; } void add_month(int n) { m+=n; } void add_day(int n) { d+=n; } void show() { cout<<y<<" "<<m<<" "<<d; } }; int main( ) { Date today; today.init(11,11,2011); Date tomorrow = today; tomorrow.add_day(1); tomorrow.show(); return 0; }
程序執行結果為:
2011 11 11
若對對象未進行初始化,則:
int main( ) { Date today; // today.init(12,12,2001); Date tomorrow = today; // warning C4700: 使用了未初始化的局部變量“today” tomorrow.add_day(1); tomorrow.show(); return 0; }
程序執行結果為:
-858993460 -858993460 -858993459
可見,程序設計的悲哀是對不確定的狀態進行了確定的操作。
一個好辦法是允許程序員聲明一個函數顯示地(the explict purpose)去初始化(initializing)對象。類的對象是這個類的一個實例,也稱為類變量。和基本數據類型變量一樣,也可以為其數據成員賦初值,不過對象的初始化比較復雜,其中最重要的方式就是構造函數(constructor)。
類的數據成員是不能在聲明類時初始化的。
class Date { int d = 1; // error C2864: “Date::d”: 只有靜態常量整型數據成員才可以在類中初始化 int m = 10; // error C2864: “Date::d”: …… int y = 1949; // error C2864: “Date::d”: …… }
如果一個類中所有的成員都是公用的,如:
如果一個類中所有的成員都是公用的,如: class Date { public: int d, m, y; } Date today = {11,11,2011}; // 將today初始化為d:11,m:11,y:2011
這和結構體變量的初始化是差不多的,但這種方法無法初始化私有成員。
class Date { private: int d, m, y; } Date today = {11,11,2011}; // error C2552:“today”: 不能用初始值設定項列表初始化非聚合
如果數據成員是私有的,該如何進行初始化?可以通過類的一個公有接口來進行“初始化”,即調用一個公共的成員函數來“初始化”,嚴格地說,這是賦值而不是初始化。
最好的方法是通過構造函數來進行初始化,類的構造函數由編譯器自動調用,而不是由程序員調用。
它承擔的任務是:實例(對象)的生成與初始化。構造函數是類中的一種特殊函數,當一個類被創建時自動被調用。
構造函數用於初始化數據成員和執行其它與創建對象有關的合適的處理過程。
構造函數是一個與其所在的類同名的函數。
class Date { int d, m, y; public: Date(int dd, int mm, int yy) // 構造函數,初始化類的私有成員d、m、y { d = dd; m = mm; y = yy; }; };
構造函數大體可分為兩類:
(1)缺省構造函數(the default constructor),無調用參數。
(2)參數化的構造函數(the parameterized constructor),有調用參數。
class Person { public: Person( ); // 缺省構造函數 Person( const string& n ); // 復制構造函數 Person(......); // 參數化的構造函數 private: string name; };
構造函數沒有返回值,連void也不行。
class Person { public: void Person( ); // error };
編譯上邊的Person類,會出現下面的錯誤提示:
error C2380: “Person”前的類型(構造函數有返回類型或是當前類型名稱的非法重定義?)
類的構造函數可以被重載(be overloaded)。但是, 每個構造函數必須有不同的函數簽名。當類的一個實例創建時,一個合適的構造函數被自動調用。一個類中可以根據需要定義多個構造函數,編譯程序根據調用時實參的數目、類型和順序自動找到與之匹配者。
class Date { int d, m, y; public: Date(int dd, int mm, int yy); Date(int dd, int mm); // today's year Date(int dd); // today's month and year Date( ); // default Date: today Date(const char* p); // date in string representation /* ... */ }; void main( ) { Date today(11); // 調用Date(int dd) Date today(11, 11); // 調用Date(int dd, int mm) Date july4("November 11, 2011"); // 調用Date(const char* p) Date now; // 調用Date( ) /* ...*/ }
在實際程序設計中,有時很難估計將來對構造函數形參的組合會有怎樣的要求,一種有效的策略是對構造函數也聲明有省缺值的形參(Default Arguments)。
例:
class Date { int d, m, y; public: Date(int dd = 0, int mm = 0, int yy = 0); /* ... */ }; int main( ) { Date today(11); // dd = 11, int mm = 0, int yy = 0 Date someDay(11, 11); // dd = 11, int mm = 11, int yy = 0 Date aDay(11, 11, 2011); // dd = 11, int mm = 11, int yy = 2011 Date now; // dd = 0, int mm = 0, int yy = 0 /* ...*/ return 0; }
在創建一類的對象數組時,對於每一個數組元素,都會執行缺省的構造函數。
#include <iostream> using namespace std; unsigned count = 0; class A { public: A ( ) { cout << "Creating A " << ++count <<endl; } }; int main( ) { A ar[3]; // 對象數組 return 0; }
執行結果為:
"Creating A " 1
"Creating A " 2
"Creating A " 3
通常將構造函數的聲明置於public中,假如將其放入private區段中會發生什么后果?這將會將構造函數成為私有的,那將意味什么?
例:將構造函數聲明為private。
class Date { private: int d, m, y; Date( ) { d = 11; m = 11; y = 2011; } }; int main( ) { Date today; // error C2248: “Date::Date”: 無法訪問 private 成員(在“Date”類中聲明) return 0; }
在上例中,將默認構造函數聲明成private,這樣便限制了無參數的Date對象創建。我們知道,當我們在程序中聲明一個對象時,編譯器為調用構造函數(如果有的話),而這個調用將通常是外部的,也就是說它不屬於class對象本身的調用,假如構造函數是私有的,由於在class外部不允許訪問私有成員,所以這將導致編譯出錯。若構造函數聲明成private,可以使用該類的友元函數或者友元類創建其對象,詳見后面友元部分。
例:這里舉一個通過構造函數來限制類對象創建的例子。(Restricting Object Creation Through Constructor)
class Emp { public: Emp( unsigned ID ) { id = ID; } //… private: unsigned id; // Emp( ); }; int main( ) { Emp elvis;// Error: no public default constructor Emp cher( 111222333 ); return 0; }
除以下兩種情況外,編譯器為一個類提供一個public型的缺省構造函數。
(1)如果一個類聲明任何一個構造函數,則編譯器不提供 public型的缺省構造函數。 如果需要有一個缺省構造函數的話,程序員必須自己編寫一個缺省構造函數。
(2)如果一個類聲明了一個非公有的缺省構造函數,則編譯器不提供缺省構造函數(如上例)。
The compiler provides a public default constructor for a class with two exception:
If a class explicitly declare any constructor, the complier does not provide a public default constructor. In this case, the programmer must provide a public default constructor if desired.
If a class declares a nonpublic default constructor, the complier does not provide a public default constructor.
class Date { int d, m, y; public: Date(int dd, int mm, int yy) { d = dd; m = mm; y = yy; } }; int main( ) { Date today = Date(12,12,2001); //OK Date this_day(12,12,2001); //OK Date my_birthday; // error C2512: “Date”: 沒有合適的默認構造函數可用 /* ...*/ return 0; }
有一個有參數化的構造函數Date(int dd, int mm, int yy),編譯器將不再提供默認的構造函數,因此創建Date對象必須給出3個int型參數,而無參數的my_birthday無法創建。