Effective C++ 學記之12 複製物件時勿忘其每一個成分
阿新 • • 發佈:2018-12-31
copying函式應該確保複製“物件內的所有成員變數”及“所有base class成分”。
這裡說的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函式共同呼叫。
不要嘗試以某個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函式共同呼叫。