复制对象时应事无巨细
Copy all parts of an object
如果你声明自己的copy函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为。编译器仿佛被冒犯似的,会以一种奇怪的方式回敬:当你的实现代码几乎必然出错时却不告诉你。
考虑一个class用来表现顾客,其中手工写出copy函数,使得外界对他们的调用会被记录下来(logged)
void logCall(const std::string& funName); //制造一个log entry
class Customer{
public:
...
Customer(const Customer& rhs); //copy构造函数
Customer& operator=(const Customer& rhs); //copy assignment操作符
...
private:
std::string name;
}
Customer::Customer(const Customer& rhs)
:name(rhs.name) //复制rhs的数据
{
logCall("Customer copy constructor");
}
Customer& Customer::operator-(const Customer& rhs)
{
logCall("Customer copy constructor");
name = rhs.name;
return *this;
}
至此,还是安然无恙的,但如果有另一个类对象加入,形势就急转直下了...
class Date {...} //日期
class Customer{
public:
... //同前
private:
std::string name;
Date lastTransacation;
}
这时候既有的copying函数执行的是局部拷贝(partial copy):它们的确复制了顾客的name,但没有复制新添加的lastTransaction。大多数编译器对此不出任何怨言。
结论很明显:如果你为class添加一个成员变量,你必须同时修改copying函数。如果你忘记,编译器不太可能提醒你。
一旦发生继承,可能会造成此一主题最暗中肆虐的一个潜藏危机。
class PriorityCustomer:publi 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 constructor");
priority = rhs.priority;
return *this;
}
PriorityCustomer的copy函数看起来好像复制了PriorityCustomer内的每一样东西,但是请看一眼。是的,它们复制了PriorityCustomer声明的成员变量,但每个PriorityCustomer还内含它所继承的Customer成员变量副本,而那些成员变量却未被复制。
因此PriorityCustomer对象的Customer成分会被不带实参的Customer构造函数(即default构造函数)初始化。
此时,你应该让derived class的copying函数调用相应的base class函数:
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:Customer(rhs), //调用base class的copy构造函数
priority(rhs.priority)
{
logCall("PriorityCustomer copy constructor");
}
PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy constructor");
Customer::operator=(rhs); //对base class成分进行赋值动作
priority = rhs.priority;
return *rhs;
}
现在应该很清楚了。当你编写一个copying函数,请确保(1)复制所有local成员变量,(2)调用所有base classes内的适当的copying函数
如果你发现你的copy构造函数和copy assigment操作符有相近的代码,你想到的不应该是令某个copying函数调用另一个copying函数,而是建立一个新的成员函数给两者调用。这样的函数往往是private而且常被命名为init。
总结:
- Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”
- 不要尝试以某个copying函数实现另一个copying函数。应该讲共同代码放进第三个函数中,并由两个copying函数共同调用
编于03/25/2019 18:47