编译器生成的拷贝函数(拷贝构造函数,拷贝赋值运算符),会拷贝对象的所有数据,当你声明自己的拷贝函数时,就是在告诉编译器,默认实现中有你不喜欢的地方。
void logCall(const std::string& funcName); // 创造一条记录class Date { ... }; // 日期class Customer {
public:...Customer(const Customer& rhs);Customer& operator=(const Customer& rhs);...
private:std::string name;Date lastTransaction; // 后期添加的成员
};Customer::Customer(const Customer& rhs): name(rhs.name) // 拷贝rhs的数据
{logCall("Customer copy constructor");
}
Customer& Customer::operator=(const Customer& rhs)
{logCall("Customer copy assignment operator");name = rhs.name; // 拷贝rhs的数据return *this; // 见条款10
}
拷贝时,后期添加的成员被遗忘了。
出现这个问题的最狡猾的方式之一是通过继承。
class PriorityCustomer : public 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 assignment operator");priority = rhs.priority;return *this;
}
在调用赋值的时候data数据由于没有进行赋值拷贝,所以在子类的赋值中缺失了该数据。
派生类复制函数必须调用它们对应的基类函数。
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs): Customer(rhs), // 调用基类拷贝构造函数priority(rhs.priority)
{logCall("PriorityCustomer copy constructor");
}
PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{logCall("PriorityCustomer copy assignment operator");Customer::operator=(rhs); // 为基类部分赋值priority = rhs.priority;return *this;
}
如果没有调用的话进行赋值的时候就会导致基类部分的数据没有赋值过去。
如果两个拷贝函数有很多重复代码,可以先构造一个,再用另一个调用它吗?不要这样做!!!构造函数会初始化新对象,但赋值运算符只适用于已经初始化的对象。
- 拷贝构造函数调用拷贝赋值运算符:对正在构造中的对象执行赋值意味着对尚未初始化的对象执行某些只对已初始化的对象有意义的操作。
- 拷贝赋值运算符调用拷贝构造函数:将试图构造一个已经存在的对象。
- 拷贝函数应该确保拷贝对象的所有数据成员和它的所有基类部分。
- 不要试图用其中一个拷贝函数来实现另一个。可以将通用功能放在第三个同时调用它们的函数中。