编译器会在必要的时候为我们的类创建copy构造函数和赋值函数,并且将被拷贝对象的所有成员变量都做一份拷贝。如果你声明自己的copy构造函数和赋值函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为。
考虑一个class用来表现顾客,其中自己写出copying函数,使得外界对它们的调用会被日志记录下来:
void logCall(const std::string& value);//日志记录
class Customer{
public:
...
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
private:
std::string m_name;
};
Customer::Customer(const Customer& rhs):m_name(rhs.m_name)
{
logCall("Customer copy constructor");
}
Customer& Customer::operator=(const Customer& rhs)
{
logCall("Customer copy assignment operator");
m_name = rhs.m_name;
return *this;
}
这里的每一件事情都很好,直到另一个成员变量的出现:
class Date{...};
class Customer{
public:
...//同上
private:
std::string m_name;
Date m_date;
};
这时候既有的copying函数执行的是局部拷贝,它们的确复制了m_name,但没有复制新添加的m_date。所以,如果你为class添加了一个成员变量,你必须同时修改copying函数,以及任何非标准形式的operator=,如果你忘记了,编译器不太可能提醒你。
一旦发生继承,可能会造成一个潜藏的危机,试考虑:
class ChildCustomer: public Customer{
public:
....
ChildCustomer(const ChildCustomer& rhs);
ChildCustomer& operator=(const ChildCustomer& rhs);
....
private:
int m_priority;
};
ChildCustomer::ChildCustomer(const ChildCustomer& rhs):m_priority(rhs.m_priority)
{
logCall("ChildCustomer copy constructor");
}
ChildCustomer& ChildCustomer::operator=(const ChildCustomer& rhs)
{
logCall("ChildCustomer copy assignment operator");
m_priority = rhs.m_priority;
return *this;
}
ChildCustomer的copying函数复制了ChildCustomer声明的成员变量,但每个ChildCustomer内还含有它继承的Customer成员变量的副本,而那先成员变量却未被复制。ChildCustomer的copy构造函数并没有指定实参传给其父类构造函数,因此ChildCustomer对象的Customer成分会被不带实参之Customer构造函数初始化。default构造函数将针对m_name和m_date执行缺省的初始化动作。
任何时候只要你承担起“为 子派生类撰写copying函数”的重责大任,你必须很小心地也复制其父类成分,那些成分往往是private,所以你无法直接访问它们,你应该让派生类的copying函数调用相应的父类函数:
ChildCustomer::ChildCustomer(const ChildCustomer& rhs)
:Customer(rhs),//调用父类的copy构造函数
m_priority(rhs.m_priority)
{
logCall("ChildCustomer copy constructor");
}
ChildCustomer& ChildCustomer::operator=(const ChildCustomer& rhs)
{
logCall("ChildCustomer copy assignment operator");
Customer::operator=(rhs);//调用父类成分进行赋值操作
m_priority = rhs.m_priority;
return *this;
}
请记住:
- copying函数应该确保复制“对象内的所以成员变量”及“所有父类成分”。
- 不要尝试以某个copying函数实现另一个copying函数。应该将共同代码部分放进第三个函数中,并由两个copying函数共同调用。