這篇文章主要介紹拷貝構造函數和賦值運算符的區別,以及在何時調用拷貝構造函數,什麼狀況下調用賦值運算符。 ios
拷貝構造函數和賦值運算符函數
在默認狀況下(用戶沒有定義,可是也沒有顯示的刪除),編譯器會自動隱式生成一個拷貝構造函數和賦值運算符,但用戶可使用delete來指定不生成拷貝構造函數和賦值運算符,這樣的對象就不能經過值傳遞,也不能進行賦值運算this
1 #include <iostream>
2
3 using namespace std; 4 class Person { 5 public: 6 Person():{} 7 Person(const Person& p) = delete; 8 Person& operator= (const Person& p) = delete; 9
10 void SetName( string strName ) 11 { 12 name = strName; 13 } 14 void SetAge(int nAge) 15 { 16 age = nAge; 17 } 18 private: 19 int age; 20 string name; 21 }; 22 int main() 23 { 24 Person p1; 25 p1.SetName("air"); 26 p1.SetAge(18); 27
28 //Person p2(p1); 錯誤的
29 return 0; 30 }
上面定義的類Person顯式的刪除了拷貝構造函數和賦值運算符,在須要調用拷貝構造函數或者賦值運算符的地方,會提示沒法調用該函數,它是已刪除的函數。spa
還有一點須要注意的是,拷貝構造函數必須以引用的方式傳遞參數,這是由於,在值傳遞給一個函數的時候,會調用拷貝構造函數生成函數的實參,若是拷貝構造函數的參數仍然是以值的方式,就會無限循環的調用下去,直到函數的棧溢出。3d
什麼時候調用code
拷貝構造函數和賦值運算符的行爲比較類似,都是將一個對象的值複製給另外一個對象,可是其結果卻有些不一樣,拷貝構造函數使用傳入對象的值生成一個新的對象的實例,而賦值運算符是將對象的值複製給一個已經存在的實例。這種區別從二者的名字也能輕易的分辨出來,拷貝構造函數也是一種構造函數,那麼它的功能就是建立一個新的對象實例;賦值運算符是執行某種運算,將一個對象的值複製給另外一個對象(已經存在的)。調用的是拷貝構造函數仍是賦值運算符,主要是看是否有新的對象實例產生,若是產生了新的對象實例,那調用的就是拷貝構造函數;若是沒有,那就是對已有的對象賦值,調用的是賦值運算符。對象
調用拷貝構造函數主要有如下場景:blog
1:對象做爲函數的參數,以值傳遞的方式傳給函數。編譯器
2:對象做爲函數的返回值,以值的方式從函數返回。string
3:使用一個對象給另外一個對象初始化。
代碼以下:
1 #include <iostream>
2
3 using namespace std; 4 class Person 5 { 6 public: 7 Person() {} 8 Person( const Person& p ) 9 { 10 cout << "Copy Constructor" << endl; 11 } 12
13 Person& operator=( const Person& p ) 14 { 15 cout << "Assign" << endl; 16 return *this; 17 } 18
19 private: 20 int age; 21 string name; 22 }; 23
24 void f( Person p ) 25 { 26 return; 27 } 28
29 Person f1( ) 30 { 31 Person p; 32 return p; 33 } 34
35
36 int main() 37 { 38 Person p; 39 Person p1 = p; // 1
40 Person p2; // 41 p2 = p; // 2
42 f(p2); // 3
43
44 p2 = f1(); // 4
45
46 Person p3 = f1(); // 5
47
48 return 0; 49 }
1:雖然使用了「=」,可是實際上使用對象p來建立了一個新的對象p1.也就是產生了新的對象,因此調用的是拷貝構造函數。
2:首先聲明瞭一個對象p2,而後使用複製運算符"=",將p的值賦值給p2,顯然是調用了賦值運算符,爲一個已經存在的對象賦值。
3:以值傳遞的方式將對象p2傳入函數f內,調用拷貝構造函數構建一個函數f可用的實參
4:這條語句拷貝構造函數和賦值運算符都調用了。函數f1以值的方式返回一個Person對象,在返回時會調用拷貝構造函數建立一個臨時對象tmp做爲返回值;返回後調用賦值運算符將臨時對象tmp賦值給p2.
5:應該是首先調用拷貝構造函數建立臨時對象;而後再調用拷貝構造函數使用剛纔建立的臨時對象建立新的對象p3,也就是會調用兩次拷貝構造函數。不過,編譯器也沒有那麼傻,應該是直接調用拷貝構造函數使用返回值建立了對象p3。