复制对象时勿忘其每一个成分
设计良好之面向对象系统会将对象的内部封装起来,只留两个函数负责对象拷贝(copy 构造函数和 copy assignment 操作符)
在 C++ 中,如果自己声明 copying 函数,会发生这样一种情况:当你的实现代码几乎必然出错时却不告诉你。
看下面的代码说事:
void logCall(const std::string& funcName); // 制造一个 log entry
class Customer {
public:
...
Customer(const Customer& r);
Customer& operator=(const Customer& r);
...
private:
std::string name;
};
Customer::Customer(const Customer& r): name(r.name){
// 复制 r 的数据
logCall("Customer copy constructor");
}
Customer& Customer::operator=(const Customer& r){
logCall("Customer copy assignment operator");
name = r.name; // 复制 r 的数据
return *this;
}
这样做看起来很好,实际也很好,但如果另一个变量的加入就会打破这个美好的局面:
class Date {
...}; // 日期
class Customer {
public:
...
private:
std::string name;
Date lastTransaction;
}
这时候上面的 copying 函数执行的就是局部构造:
复制了 name,但是没有复制新添加的 lastTransaction。但大多数的编译器都不会报错。
一但发生继承,将面临这一主题最暗中肆虐的危机:
class vipCustomer: public Customer {
// 一个 derived class
public:
...
vipCustomer(const vipCustomer& r);
vipCustomer& operator=(const vipCustomer& r);
private:
int vip;
};
vipCustomer::Customer(const vipCustomer& r): vip(r.vip){
// 复制 r 的数据
logCall("vipCustomer copy constructor");
}
vipCustomer& vipCustomer::operator=(const vipCustomer& r){
logCall("vipCustomer copy assignment operator");
vip = r.vip; // 复制 r 的数据
return *this;
}
vipCustomer 的 copying 函数只是复制了 vipCustomer 声明的成员变量,但是它还含有从 base class 继承的成员变量附件(副本),但没有被复制。因此 base class 的成员变量保持不变。
在为 derived class 撰写 copying 函数时,都必须小心地也复制其 base class 的成分。那些成分往往是 private 的,所以应该让 derived class 的 copying 函数调用相应的 base class 函数:
vipCustomer::Customer(const vipCustomer& r):
Customer(r); // 调用 base class 的 copy 构造函数
vip(r.vip){
// 复制 r 的数据
logCall("vipCustomer copy constructor");
}
vipCustomer& vipCustomer::operator=(const vipCustomer& r){
logCall("vipCustomer copy assignment operator");
Customer::operator(r); // 对 base class 成分进行赋值操作
vip = r.vip; // 复制 r 的数据
return *this;
}
上面只是简单介绍,详细内容会在条款22细说。
copy 构造函数和 copy assignment 操作符不能相互调用!
最后记住:
- Copying 函数应该确保复制 “ 对象内的所有成员变量 ” 及 “ 所有 base class 成分 ”。
- 不要尝试以某个 copying 函数实现另一个 copying 函数。应该将共同机能放进第三个函数中,并由两个 copying 函数共同调用。