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

原文地址:https://www.cnblogs.com/lidan/p/2322098.html