每一次都會忘,做個筆記吧。想到哪里寫到哪里。
拷貝構造函數
-
第一個參數必須是自身類類型的引用,且任何額外參數都有默認值。(為什么必須是引用?見后解釋)
-
合成拷貝構造函數:如果我們沒有為一個類定義拷貝構造函數,則編譯器會為我們定義一個。同合成的默認構造函數不同的是,即使我們定義了其他構造函數,編譯器也會為我們合成一個拷貝構造函數。(一旦自己定義了構造函數,則不會合成默認構造函數)
-
拷貝初始化與直接初始化
- 直接初始化:要求編譯器使用普通的函數匹配來選擇與我們提供的參數最匹配的構造函數。
- 拷貝初始化:要求編譯器將右側運算對象拷貝到正在創建的對象中,如果需要的話,還要進行類型轉換。
string dots(10, '.'); //直接初始化 string s(dots); //直接初始化 string s2 = dots; //拷貝初始化 string null_book = "9-999-8999"; //拷貝初始化 string nines = string(100, '9'); //拷貝初始化
-
使用‘=’號的是拷貝初始化,不使用等號的是直接初始化。
-
拷貝初始化發生在以下情況
1. 用 = 定義變量時發生。 2. 將一個對象作為實參傳遞給一個非引用類型的形參。 3. 從一個返回類型為非引用類型的函數返回一個對象。 4. 用花括號列表初始化一個數組中的元素或一個聚合類中的成員。(聚合類是指沒有用戶定義的構造函數,沒有私有和保護的非靜態數據成員,沒有基類,沒有虛函數)。
-
拷貝構造函數第一個參數必須是引用原因:由於拷貝構造函數被用來初始化非引用類類型的參數。如果其自身參數不是引用類型,則調用永遠也不會成功——為了調用拷貝構造函數,我們必須拷貝它的實參,但為了拷貝實參,我們又必須調用拷貝構造函數,如此無限循環。
拷貝賦值運算符
- 與類控制其對象如何初始化一樣,類也可以控制器對象如何賦值:
與拷貝構造函數一樣,如果類未定義自己的拷貝賦值運算符,編譯器也會為它合成一個。Sales_data trans, accum; trans = accum; //使用Sales_data的拷貝賦值運算符
- 重載賦值運算符
- 重載運算符本質上是函數,其名字由operator關鍵字后接表示要定義的運算符的符號組成。因此,賦值運算符就是一個名為operator=的函數。類似於任何其他函數,運算符函數也有一個返回類型和一個參數列表。
- 如果是一個運算符是一個成員函數,其左側運算對象就綁定到隱式的this參數。對於一個二元運算符,例如賦值運算符,其右側運算對象作為顯式參數傳遞。
- 拷貝賦值運算符接受一個與其類相同類型的參數:
為了與內置類型的賦值保持一致,賦值運算符通常返回一個指向其左側運算對象的引用。注意,標准庫通常要求保存在容器中的類型要有其賦值運算符,且其返回值是左側運算對象的引用。class Foo{ public: Foo& operator=(const Foo&); //賦值運算符 //... };
析構函數
-
析構函是類的一個成員函數,名字由波浪號接類名構成。它沒有返回值,也不接受參數:
class Foo{ public: ~Foo(); //析構函數 //... };
由於析構函數不接受參數,因此它不能被重載。對於一個給定類,只會由唯一一個析構函數。
-
在一個構造函數中,成員的初始化時在函數體執行之前完成的,且按照它們在類中出現的順序進行初始化。在一個析構函數中,首先執行函數體,然后銷毀成員。成員按初始化順序的逆序進行銷毀。
-
無論何時一個對象被銷毀,就會自動調用其析構函數:
1. 變量在離開其作用域時被銷毀 2. 當一個對象被銷毀時,其成員被銷毀 3. 容器(無論是標准容器還是數組)被銷毀時,其元素被銷毀 4. 對於動態分配的對象,當對指向它的指針應用delete運算符時被銷毀 5. 對於臨時對象,當創建它的完整表達式結束時被銷毀