在C++的類中,都會有一個或多個構造函數、一個析構函數、一個賦值運算操作符。即使我們自己定義的類中,沒有顯示定義它們,編譯器也會聲明一個預設構造函數、一個析構函數和一個賦值運算操作符。例如: 1 //聲明一個空類 2 class Empty{}; 3 4 //但是這個空類和下麵這個類是等同的 5 c
在C++的類中,都會有一個或多個構造函數、一個析構函數、一個賦值運算操作符。即使我們自己定義的類中,沒有顯示定義它們,編譯器也會聲明一個預設構造函數、一個析構函數和一個賦值運算操作符。例如:
1 //聲明一個空類 2 class Empty{}; 3 4 //但是這個空類和下麵這個類是等同的 5 class Empty 6 { 7 Empty(){.....}; //預設構造函數 8 Empty( const Empty & rhs ){......} //複製構造函數 9 ~Empty(){..........} //析構函數 10 11 Empty operator * (const Empty & rhs){.........} 12 };
值得註意的是,只有當這些函數被調用的時候,他們才會被編譯器創建出來。
如果我們已經聲明瞭一個構造函數,那麼編譯器將不會再創建一個預設構造函數。
下麵來詳細介紹一下構造函數和析構函數。
1.構造函數
構造函數(constructor)是與類同名的特殊成員函數,主要用於初始化對象的數據成員。定義形式如下:
1 class X 2 { 3 // ....... 4 X(); //無參構造函數 5 X(......); //有參構造函數,構造函數支持重載 6 7 // ......... 8 }
構造函數的聲明和定義方法與類的其他成員函數相同,可以在類的內部定義構造函數,也可以先在類中聲明構造函數,然後在類外進行定義。在類外定義構造函數的形式如下:
1 X :: X(.....) 2 { 3 //........... 4 }
構造函數具有以下幾個特點:
(1)構造函數與類同名
(2)構造函數沒有返回類型,void也不行
(3)構造函數可以被重載
(4)構造函數由系統自動調用,不允許在程式中顯示調用它
(5)構造函數的調用時機是定義對象之後的第一時間,即構造函數是對象的第一個被調用的函數
(6)定義對象數組或用new創建動態對象時,也要調用構造函數。但定義數組對象時,必須有無參構造函數
(7)構造函數通常應定義為共有成員(當然也可以定義為私有的,但不能被類外部訪問。單例模式就用到私有化的構造函數)
同時還需註意一下幾點:
(1)構造函數初始化列表中的成員初始化次序與它們在類中聲明的次序相同,與其在初始化列表中的次序無關。如:
1 Tdate::Tdate(int m , int d , int y):month(m),day(d),year(y){} 2 Tdate::Tdate(int m , int d , int y):year(y),month(m),day(d){} 3 Tdate::Tdate(int m , int d , int y):day(d),year(y),month(m){} 4 //以上三個構造函數完全相同
(2)構造函數初始化列表先於構造函數體中的語句執行
(3)以下類成員必須使用成員初始化列表進行初始化:常量成員、引用成員、類對象成員、派生類構造函數對基類構造函數的調用
1.1預設構造函數
預設構造函數是指不需要顯示提供參數的構造函數。在某些情況下,必須使用預設構造函數來定義對象(如對象數組)
如果一個類沒有定義任何構造函數,在需要時編譯器將會為它生成一個預設構造函數,它只負責創建對象,不做任何初始化工作。
在用預設構造函數創建對象時,如果創建的是全局對象或靜態對象,則對象的位模式全為0(可以理解為將所有數據成員初始化為0);如果創建的是局部對象,不進行對象數據成員的初始化,對象數據成員是未知的。
註意:只有在類沒有定義任何構造函數時,系統才會產生預設構造函數。一旦定義了任何形式的構造函數,系統將不再產生預設構造函數。
我們可以對預設構造函數進行重定義,來為對象的數據成員提供初始值。同時我們還可以為參數提供預設值。
1.2重載構造函數
重載構造函數必須具有不同的函數原型(即參數個數、參數類型或參數次序不能完全相同)
1.3複製構造函數
複製構造函數是一個特殊的構造函數,用於根據已存在的對象初始化一個新建對象。
如果沒有定義類的複製構造函數,在需要時,C++編譯器將產生一個具有最小功能的預設複製構造函數,形式如:X::X(const X&){}
預設複製構造函數以成員按位複製(bit-by-bit)的方式實現成員的複製。按位複製就是把一個對象各數據成員的值原樣複製到目標對象中。在沒有涉及指針類型的數據成員時,預設構造函數能夠很好地工作。但是,當一個類有指針類型的數據成員時,預設複製構造函數常會產生指針懸掛問題。如果類存在指針類型的數據成員,就應該為他提供自定義的複製構造函數(也就是重載)
註意:複製構造函數的參數常常是const類型的本類對象的引用(X::X(const X&){})
2.析構函數
析構函數(destructor)是與類同名的另一個特殊成員函數,作用於構造函數相反,用於在對象生存期結束時完成對象的清理工作。
析構函數的名字由“~”+“類名”構成,形式如下:
1 class X 2 { 3 private: 4 //............ 5 public: 6 ~X(); //析構函數 7 X(); //無參構造函數 8 X(....); // 有參重載的構造函數 9 //......... 10 }
析構函數具有以下特點:
(1)析構函數的名字是在類名前加上“~”,不能是其他名字
(2)析構函數沒有返回值類型(void也不行),沒有參數表
(3)析構函數不能重載,一個類只能有一個析構函數
(4)析構函數只能由系統自動調用,不能再程式中顯示調用析構函數
(5)若有多個對象同時結束生存期,C++按照與調用構造函數相反的次序調用析構函數
(6)每個類都應該有一個析構函數,如果沒有顯示定義析構函數,C++將產生一個最小化的預設析構函數(X::~X())
(7)構造函數和析構函數都可以是inline函數
(8)在通常情況下,析構函數應設置為公有成員