1. 程式人生 > >effect C++ 複製物件時勿忘其每一個成分

effect C++ 複製物件時勿忘其每一個成分

copy 函式

設計良好之面向物件系統會將物件的內部封裝起來,只留兩個函式負責物件拷貝(複製),copy建構函式和co培養assignment 操作符。

如果你宣告自己的copying函式,意思是告訴編譯器你並不喜歡預設實現中的某些行為。編譯器會以一種奇怪的方式回敬:當你的實現程式碼幾乎必然出錯卻不告訴你

void logCall(const std::string& funcName);
class Customer{
public :
  ...
 Customer(const Customer&rhs);
 Customer& operator=(const Customer &rhs);
 ...
 private:
 std::string name;
};
Customer::Customer(const Customer &rhs):name (rhs.name)
{
  logCall("Customer copy constructor");
}
Customer &Customer::operator=(const Customer&rhs)
{
  logCall("Customer copy assignment operator");
  name = rhs.name;
  return *this;
}

如果另一個成員變數加入戰局
class Date{...};
class Customer{
public:
   ...       //同前
private:
  std::string name;
  Date lastTransaction;  
}
這時既有的copying函式執行的是區域性拷貝:複製了name,沒有新新增複製的lastTransaction。編譯器不會發生怨言。

如果你為class新增一個變數,你必須同時修改copy函式(還有class內所有建構函式以及任何非標準形式的operator=)

一旦發生繼承

 class PriorityCustomer:public Customer{
 public:
  ...
  PriorityCustomer(const PriorityCustomer &rhs);
  PriorityCustomer& operator=(const PriorityCustomer &rhs);
  ...
  private:
  int priority;
 };
 PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):priority(rhs.priority)
 {
   logCall("PriorityCustomer copy constructor");
 }
  PriorityCustomer&PriorityCustomer::operator=(const PriorityCustomer &rhs)
 {
    logCall("PriorityCustomer copy assignment operator");
    priority = rhs.priority;
   return *this;
 }

PriorityCustomer的copying函式複製了PriorityCustomer內宣告的成員變數,但每個PriorityCustomer還內涵它所繼承的Customer成員變數復件(副本),而那些成員變數並未複製。PriorityCustomer的copy建構函式並沒有指定的實參傳給base class建構函式(也就是說它在它的成員初值列中沒有提到Customer),因此PriorityCustomer物件Customer成分會被不帶實參之Customer建構函式(即default建構函式)初始化。default建構函式將針對name和lastTransaction執行預設的初始化動作。

任何時候需要“為derived class 撰寫copying函式”的重責大任,必須複製其base class 成分。應該用derived class 的copying函式呼叫相應的base class函式。

 PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority)
 {
   logCall("PriorityCustomer copy constructor");
 }
  PriorityCustomer&PriorityCustomer::operator=(const PriorityCustomer &rhs)
 {
    logCall("PriorityCustomer copy assignment operator");
    Customer::operator=(rhs);
    priority = rhs.priority;
   return *this;
 }
當編寫一個copying函式,請確保 (1)複製所有local成員變數 (2)呼叫所有base class 內的適當的copying函式

如果copy函式和copy assignment操作符有相近的程式碼,消除重複程式碼的做法是,建立一個新的成員函式給兩者呼叫。這樣的函式往往是private而且常被命名為init.

Copying函式應該確保複製“物件內所有成員變數”及“所有base class”成分。

不要嘗試以某個copy函式實現另一個copy函式。應該將共同機能放進第三個函式中,並由兩個coping函式共同呼叫。