//來自於筆記整理。
類在使用時,需用到構造函數。
構造函數是與類同名的函數,如
class Node
{
Node()
{
函數體
}
};
先可以理解爲定義結構體時會通過Node()這個函數來初始化類,接下來會有更準確的解釋。
如果沒有編寫構造函數,則系統會自動創建構造函數,此構造函數沒有參數,函數爲空,即什麼都不做。
在使用時,常常不止一個構造函數,適用於函數重載原理(函數名相同,參數列表不同,系統通過參數列表來判斷選定函數)。
構造函數中有一類特別的,叫做拷貝構造函數,也稱爲複製構造函數,這個函數也是必須存在的,如果沒有編寫拷貝構造函數,系統會自動生成一個,但是如果類中有指針變量成員,此時會存在風險(淺拷貝,接下來會有解釋),所以良好的編寫習慣是類中編寫構造函數與拷貝構造函數。
複製構造函數中有一個形參,該形參是所在類 類型的引用,如
class Node
{
Node(const Node&);
};
形參常用const修飾。
接下來引申一下初始化的過程。
類的初始化有兩種,複製初始化和直接初始化,簡單來區分,就是複製初始化用=,直接初始化用(),但是在本質上,有較大的區別。
以下情況,拷貝構造函數會被調用:
- 一個對象以值傳遞的方式傳入函數體
- 一個對象以值傳遞的方式從函數返回
- 一個對象需要通過另一個對象進行初始化
自定義拷貝構造函數是一種良好的習慣,可以提高編碼效率,並減少風險(淺拷貝)。
直接初始化的實現是直接調用與實參匹配的構造函數,複製初始化總是調用複製構造函數(更準確地說,總是需要用到構造函數),複製初始化是使用指定的構造函數(注意,這裏不一定是複製構造函數)創建一個臨時對象,再利用複製構造函數將臨時對象複製到目標函數,所以如果你自己定義複製構造函數並設爲private,那麼所有的複製初始化都無法執行,系統自動創造的複製構造函數是public的。
由於複製初始化總等價於直接調用複製構造函數進行初始化,所以編譯器常常會對複製初始化進行優化。
這也就造成了複製初始化與直接初始化的效率差異,通常來說,直接初始化與複製初始化僅在低級別的優化上存在差異,但對於不支持複製的類型,就擁有本質區別。
最後談談不編寫複製構造函數的危險性,即淺拷貝(位拷貝)與深拷貝。
如果由系統自動生成複製構造函數,且該構造函數完成對象與對象之間的拷貝,那麼就叫淺拷貝。
如果實行淺拷貝,如A=B,B中存在一個指針變量成員,並且已經申請了內存,那A中的指針變量成員就指向同一塊內存,如果B中指針成員內存被釋放,那A會成爲野指針。
故換個角度說,如果一個類擁有資源,當類複製過程中,資源沒有重新分配,就是淺拷貝。相反,如果資源重新分配(如申請內存),則爲深拷貝。