当前位置: 代码迷 >> 综合 >> 条款12:复制对象时勿忘其每一个成分
  详细解决方案

条款12:复制对象时勿忘其每一个成分

热度:70   发布时间:2023-10-22 15:12:36.0

  设计良好之面向对象系统会将对象的内部封装起来,只留下两个函数负责对象拷贝(复制),那便是带着适切名称的copy构造函数和copy assignment操作符,称之为copying函数。

  考虑一个class用来表现顾客,其中手工写出(而非编译器创建)copying函数,使得外界对它们的调用会被记录下来:

void logCall(const std::string& funcName);            //制造一个log entry
class Customer
{
public:...Customer(const Customer& rhs);Customer& operator=(const Custtomer& rhs);...
private:std::striing name;
};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;                            //允许连续赋值
}

  这里的每一事情看起来都很好,直到另一个变量的加入。

class Data{...};                    //日期
class Customer 
{
public:...
private:std::string name;Date lastTransaction;
};

  这时候既有的copying函数执行的是局部拷贝:它们的确复制了顾客的name,但是没有复制新添加的lastTransaction。大多数编译器不会对此做出任何怨言。所以,如果你为class添加一个成员变量,你必须同时修改copying函数。(你也需要修改class的所有构造函数衱任何非标准形式的operator=)。

  一旦发生继承,可能会造成另一个潜在危机。

class PriorityCustomer:public Customer                //一个derived class 
{
public:...PriorityCustomer(const PriorityCustomer& rhs);PriorityCustomer operator=(const PriorityCustomer& rhs);...
private:int priority;
};PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:priority(rhs.priority)
{logCall("PriorityCustomer copy constrctructor");
}PriorityCustomer& PriorityCustomer:: operator=(const PriorityCustomer& rhs)
{logCall(“PriorityCustomer copy assignment operator”);priority = rhs.priority;return *this;
}

  PriorityCustomer的copying函数看起来似乎复制了PriorityCustomer内的每一样东西。但是,它是仅仅复制PriorityCustomer声明的成员变量,但每个PriorityCustomer还内含有它所继承的Customer成员变量复件,而那些成员变量却从未被复制。PriorityCustomer的copy构造函数并没有指定实参传给其base class构造函数,因此PriorityCustomer对象的Customer成分会被不带实参之Customer即default构造函数初始化。default构造函数即将针对name和lastTransaction执行缺省的初始化动作。

  当我们尝试为derived class撰写copying函数时,必须很小心的也复制其base class成分。那些成分往往是private,所以你无法访问它们,你应该让derived class的copying函数调用相应的base class函数:

PriorityCustomer::PriorityCustomer(const PriorityCustoer& rhs)
:Customer(rhs),priority(rhs.priority)                             //调用base class的copy构造函数
{logCall("PriorityCustomer copy constructor");  
}PriorityCustomer& Prioritycustomer::operator=(const PriorityCustome& rhs)
{logCall("PriorityCustomer copy assignment operator");Customer::operator=(rhs);                       //对base class成分进行赋值操作priority = rhs.priority;               return *this;  
}

 

  这两个copying函数往往有近似相同的实现本体,这可能会诱使你让某个函数调用另一个函数一避免代码重复。

  令copying操作符调用copy构造函数时不合理的,因为这就像试图构造一个已经存在的对象。

  反方向--令copy构造函数调用copy assignment操作符--同样无意义。构造函数用来初始化新对象,而assignment操作符只施加于已经初始化对象身上。

 

  结论:

  (1)copying函数应该确保复制“对象内所有成员变量”及“所有base class 成分”。

  (2)不要尝试以某个copying函数实现另一个copying函数。应该讲共同机能放进第三个函数中,并由两个copying函数共同调用。

  相关解决方案