在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 CTestCopy Constructor of CTestCopy Constructor of CTestAssign 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++