1. 程式人生 > >Effective C++ 學記之12 複製物件時勿忘其每一個成分

Effective C++ 學記之12 複製物件時勿忘其每一個成分

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

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


這裡說的copying函式指的是“copy建構函式”和“copy賦值操作符”。前面的條款講到,當用戶自己定義copying函式時,編譯器不會生成預設的copying函式。
如下面的例子:

void logCall(const std::string& funcName); //製造一個log 入口
class  Customer{
public:
    ...
    Customer(const Customer& rhs);//copy建構函式
    Customer& operator=(const Customer& rhs);//copy賦值操作符
    ...
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的copy,沒copy新新增的lastTransaction。編譯器也不會報錯。
因此:如果你為class新增一個成員變數,必須同時修改copying函式。


一旦發生繼承,也會出現麻煩:
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 assighment operator");
    priority = rhs.priority;
    return *this;
}
由於上面PriorityCustomer的copying函式並未制定實參傳給起base class建構函式,因此PriorityCustomer的Customer成分會被不帶實參的default建構函式初始化。
name和lastTransaction將被預設初始化。

解決方案:讓derived class的copying函式呼叫相應的base class函式:

PriorityCustomer:PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority)//調base class的copy建構函式
{
    logCall("PriorityCustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::operator(const PriorityCustomer& rhs)
{
    logCall("PriorityCustomer copy assighment operator");
    Customer::operator=(rhs);//對base class進行賦值動作
    priority = rhs.priority;
    return *this;
}
編寫一個copying函式請把握好:
1 複製所有local成員變數。
2 調所有base classes內的適當copying函式。

另外不該令copy assignment操作符呼叫copy建構函式,因為這樣會像試圖構造一個已經存在的物件,是不合理的。。應該將共同機能放進第三個函式中,並由兩個copying函式共同呼叫。