class Person { public: Person(const Person& p); Person& operator=(const Person& p); private: int age; string name; };
在默認情況下(用戶沒有定義,但是也沒有顯式的刪除),編譯器會自動的隱式生成一個拷貝構造函數和賦值運算符。
但用戶可以使用delete
來指定不生成拷貝構造函數和賦值運算符,這樣的對象就不能通過值傳遞,也不能進行賦值運算。
Person(const Person& p) = delete;
注意:拷貝構造函數必須以引用的方式傳遞參數。這是因為,在值傳遞的方式傳遞給一個函數的時候,會調用拷貝構造函數生成函數的實參。
如果拷貝構造函數的參數仍然是以值的方式,就會無限循環的調用下去,直到函數的棧溢出。
(不用引用,編譯不能通過)
何時調用
拷貝構造函數和賦值運算符的行為比較相似,都是將一個對象的值復制給另一個對象;但是其結果卻有些不同,
拷貝構造函數使用傳入對象的值生成一個新的對象的實例,而賦值運算符是將對象的值復制給一個已經存在的實例。
這種區別從兩者的名字也可以很輕易的分辨出來,拷貝構造函數也是一種構造函數,那么它的功能就是創建一個新的對象實例;賦值運算符是執行某種運算,將一個對象的值復制給一個已經存在的實例。
調用的是拷貝構造函數還是賦值運算符,主要是看是否有新的對象實例產生。如果產生了新的對象實例,那調用的就是拷貝構造函數;如果沒有,那就是對已有的對象賦值,調用的是賦值運算符。
調用拷貝構造函數主要有以下場景:
- 對象作為函數的參數,以值傳遞的方式傳給函數。
- 對象作為函數的返回值,以值的方式從函數返回
- 使用一個對象給另一個對象初始化
A print(int n) { A a(1); return a; } A aa = print(2332);
下方的示例一樣,
A print(int n) { A a(1); return a; } print(2332);
不管有沒有設置返回值,在返回時都會調用拷貝構造函數: