在C++中,調用拷貝構造函數有三種情況:
1.一個對象作為函數參數,以值傳遞的方式傳入函數體.
2.一個對象作為函數返回值,以值傳遞的方式從函數返回.
3.一個對象用於給另外一個對象進行初始化(復制初始化).
拷貝構造函數必須以引用的形式傳遞(參數為引用值).其原因如下:
當一個對象以傳遞值的方式傳一個函數的時候,拷貝構造函數自動的調用來生成函數中的對象.
這樣會導致無限循環地調用拷貝構造函數,直至棧溢出.
以前,一直有個誤解,以為以同類型的對象調用"="時,就會調用賦值符.參看以下的例子:
1 class CTest { 2 public: 3 CTest(); 4 CTest(const CTest&); 5 CTest& operator=(const CTest &); 6 }; 7 CTest::CTest() 8 { 9 cout<<"Constructor of CTest"<<endl; 10 } 11 CTest::CTest(const CTest& arg) 12 { 13 cout<<"Copy Constructor of CTest"<<endl; 14 } 15 CTest& CTest::operator=(const CTest& arg) 16 { 17 cout<<"Assign function of CTest"<<endl; 18 } 19 int main() 20 { 21 CTest a; 22 CTest b(a); 23 CTest c = a; 24 a = c; 25 return 0; 26 }
按照以前的理解,第21~24行代碼,應該分別調用構造函數,拷貝構造函數,賦值符函數,賦值符函數.
然而最終如下,不是如自己所想...說明以前的理解是錯誤的.
Constructor of CTest
Copy Constructor of CTest
Copy Constructor of CTest
Assign function of CTest
第23行代碼調用的是拷貝構造函數,不是賦值符函數,但第24行代碼調用的賦值符函數,不是拷貝構造函數.原因如下:
拷貝構造函數創建新的對象,而賦值符函數不創建新對象,它要求"="的左右對象均已存在,它的作用就是把"="右邊的對象的值賦給左邊的對象.
雖然編譯器會提供拷貝構造函數和賦值符函數,但是有時候編譯器提供的這些函數,並不能滿足我們的需求,因而需要自定義拷貝構造函數和賦值函數.
這里就會引出一個新問題,什么時候需要自定義拷貝構造函數和賦值符函數.
簡單的規則:如果需要定義一個非空的析構函數,那么,通常情況下也需要定義一個拷貝構造函數和賦值符函數.
通常的原則是:
1.對於凡是包含動態分配成員或包含指針成員的類都應該提供拷貝構造函數;
2.在提供拷貝構造函數的同時,還應該考慮重載"="賦值操作符,即提供賦值符函數.
當我們知道需要自定義拷貝構造函數和賦值符函數時,就得考慮如何良好的實現它們.
當自定義copying函數(包含拷貝構造函數和賦值符函數)時,需要確保以下兩點:
1.復制所有的local成員變量
2.調用所有base classes內的適當的copying函數,完成基類的copying.
下面是一個具體的例子:
1 void logcall(const std::string& funcName); //制造一個log entry 2 class Customer { 3 public: 4 ... 5 Customer(const Customer& rhs); 6 Customer& operator=(const Customer& rhs); 7 ... 8 private: 9 std::string name; 10 }; 11 Customer::Customer(const Customer& rhs):name(rhs.name) 12 { 13 logCall("Customer copy constructor"); 14 } 15 Customer& Customer::operator=(const Customer& rhs) 16 { 17 logCall("Customer copy assignment operator"); 18 name = rhs.name; //疑惑,為什么在copying函數里可以通過對象調用私有變量? 19 return *this; 20 } 21 22 class PriorityCustomer:public Customer { 23 public: 24 ... 25 PriorityCustomer(const PriorityCustomer& rhs); 26 PriorityCustomer& operator=(const PriorityCustomer& rhs); 27 ... 28 private: 29 int priority; 30 }; 31 PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority) 32 { 33 logCall("PriorityCustomer copy constructor"); 34 } 35 PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs) 36 { 37 logCall("PriorityCustomer copy assignment operator"); 38 Customer::operator=(rhs); //對base class成分進行賦值動作 39 priority = rhs.priority; 40 return *this; 41 }
第18行代碼中,通過對象調用私有變量,似乎違背了私有變量的含義,有點無法理解,具體的分析和理解,請參考:
http://www.cnblogs.com/dwdxdy/archive/2012/07/17/2595741.html
使用opertator=函數給對象賦值時,若右邊的對象和調用對象相同,即自我賦值,會引發自我賦值的不安全問題.具體分析和解決方案,請參考:
http://www.cnblogs.com/dwdxdy/archive/2012/07/17/2595821.html
參考資料:http://baike.baidu.com/view/1266959.htm
參考資料:Effective C++