条款12:复制对象时勿忘其每一个成分
设计良好的OO系统会将对象的内部封装起来,只留两个函数负责对象拷贝,那便是copy构造函数和copy assignment操作符。
我们知道,如果不给类提供一个copy函数,则当需要时,编译器会为我们提供copy函数。
但是,如果你自己声明了copy函数,就是在拒绝了编译器为你提供的版本。意思是你不喜欢编译器所提供的版本,此时,如果它好像是被冒犯了一样,如果你的代码出现了错误,它也不提醒你。
classCustomer{
...
Customer(constCustomer&rhs);
Customer&operator=(constCustomer&rhs);
...
private:
std::stringname;
};
Customer::Customer(constCustomer&rhs):name(rhs.name){ }
Customer::operator=(constCustomer&rhs){
name=rhs.name;
return*this;
}
我们忽略第10条款所讲的内容,这是一个很好的程序,但是当我们的class发生变化时,灾难来了。比如我们的class加入一个新的数据成员:
classDate{};
classCustomer{
...
Customer(constCustomer&rhs);
Customer&operator=(constCustomer&rhs);
...
private:
std::stringname;
DatelastTransaction
};
而我们忽略了copy函数的修改,灾难就来了。因为,我们只对Customer对象的局部数据成员(name)进行了复制,而对lastTransaction成员没有操作,所以会使此数据成员未被初始化。
classPriorityCustomer:publicCustomer{
public:
PriorityCustomer(constPriorityCustomer&rhs);
PriorityCustomer&operator=(constPriorityCustomer&rhs);
private:
intpriority;
};
PriorityCustomer::PriorityCustomer(constPriorityCustomer&rhs):priority(rhs.priority){}
PriorityCustomer&PriorityCustomer::operator=(constPriorityCustomer&rhs){
this->priority=rhs.priority;
return*this;
}
你会发现,此时的copy函数并没有对基类成员的初始化。因此隐式地会调用基类的无参的默认的构造函数,也就导致了基类局部成员的未初始化。为了避免此灾难,我们要显式的对基类成员进行初始化工作。
PriorityCustomer::PriorityCustomer(constPriorityCustomer&rhs):priority(rhs.priority),Customer(rhs){}
PriorityCustomer&PriorityCustomer::operator=(constPriorityCustomer&rhs){
Customer::operator=(rhs);
this->priority=rhs.priority;
return*this;
}
因此,当你编写一个copy函数时,请确保:
1) 复制所有的local成员变量
2) 调用所有base classes内的适当的copying函数。
注意以下两点:
1) 令赋值操作符调用拷贝构造函数是不合理的。
2) 令拷贝构造函数调用赋值操作符也是不合理的。
如果你发现你的赋值操作符与拷贝构造函数有太多的相似代码,消除重复的方法是,建立一个新的成员函数供它们调用。这样的函数往往是private而且常命名为init。
请记住:
n Copying函数应该确保复制“对象内的所有成员变量”及“所有base class 成员”
n 不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。