effective C++ 条款 12:复制对象时勿忘其每一个成分
copy构造函数和copy assignment操作符,我们称他们为copying函数。这些“编译器生成版”的行为:将被烤对象的所有成员变量
都做一份拷贝。
声明自己的copying函数,
void logCall(const std::string funcName);
class Customer
{
public:
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
protected:
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:
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
protected:
private:
std::string name;
Date lastTransaction;
};这时既有的copying函数就是局部拷贝:他们的确复制了name但没有复制新添加的lastTransaction。如果你为class添加一个成员变量,你必须同时修改copying函数。(你也需要修改所有的构造函数以及任何非标准形式的operator=)。
一旦发生继承,可能会造成此一主题最暗中肆虐的一个潜藏危机。试考虑:
class PriorityCustomer : public Customer
{
public:
PriorityCustomer(const PriorityCustomer& rhs);
PriorityCustomer& operator=(const PriorityCustomer& rhs);
protected:
private:
int priority;
};PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:priority(rhs.priority)
{
logCall("PriorityCustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy assignment operator");
priority = rhs.priority;
return *this;
}PriorityCustomer的copying函数看起来像复制了PriorityCustomer内的每一样东西,但是PriorityCustomer中还内含了Customer成员变量的副本,而那些却未被复制,PriorityCustomer的copy构造函数并没有指定实参传给其base class构造函数,因此PriorityCustomer对象的Customer成分会被不带实参的default构造函数初始化。default构造函数将对name和lastTransaction执行缺省的初始化动作。
base class 的成分往往是private, 所以你无法直接访问他们,你应该让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 assignment operator");
Customer::operator =(rhs); //对base class成分进行赋值动作
priority = rhs.priority;
return *this;
}
当你编写一个copying函数,请确保1.复制所有的local成员变量,2.调用所有的base class内的适当的copying函数。
当这两个copying函数有近似相同的实现本体,令一个copying函数调用另一个copying函数无法让你达到你想要的目标。
令copy assignment调用copy构造函数是不合理的,因为这像试图构造一个已经存在的对象。
反方向令copy构造函数调用copy assignment操作符,同样无意义,相当于在一个未构造好的对象赋值。
消除重复代码的做法是:建立一个新的成员函数给两者调用。这样的函数往往是private而且常被命名为init。