C++条款:复制对象时应事无巨细 3/55

本文讨论了在C++中编写复制构造函数和赋值操作符时,必须确保复制对象的所有部分,包括所有成员变量和基类成分。如果忘记复制新增成员变量,编译器不会给出警告,可能导致问题。同时,当存在继承时,派生类的复制函数应调用基类的相应复制函数。如果这两个函数有相似的代码,应将共享代码提取到一个私有辅助函数中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复制对象时应事无巨细

Copy all parts of an object

如果你声明自己的copy函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为。编译器仿佛被冒犯似的,会以一种奇怪的方式回敬:当你的实现代码几乎必然出错时却不告诉你。

考虑一个class用来表现顾客,其中手工写出copy函数,使得外界对他们的调用会被记录下来(logged)

void logCall(const std::string& funName);    //制造一个log entry
class Customer{
public:
    ...
    Customer(const Customer& rhs);    //copy构造函数
    Customer& operator=(const Customer& rhs);    //copy assignment操作符
    ...
private:
    std::string name;
}
Customer::Customer(const Customer& rhs)
    :name(rhs.name)    //复制rhs的数据
{
    logCall("Customer copy constructor");
}
Customer& Customer::operator-(const Customer& rhs)
{
    logCall("Customer copy constructor");
    name = rhs.name;
    return *this;
}

至此,还是安然无恙的,但如果有另一个类对象加入,形势就急转直下了...

class Date {...}    //日期
class Customer{
 public:
    ...             //同前
 private:
    std::string name;
    Date lastTransacation;
}

这时候既有的copying函数执行的是局部拷贝(partial copy):它们的确复制了顾客的name,但没有复制新添加的lastTransaction。大多数编译器对此不出任何怨言。

结论很明显:如果你为class添加一个成员变量,你必须同时修改copying函数。如果你忘记,编译器不太可能提醒你。

一旦发生继承,可能会造成此一主题最暗中肆虐的一个潜藏危机。

class PriorityCustomer:publi 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 constructor");
    priority = rhs.priority;
    return *this;
}

PriorityCustomer的copy函数看起来好像复制了PriorityCustomer内的每一样东西,但是请看一眼。是的,它们复制了PriorityCustomer声明的成员变量,但每个PriorityCustomer还内含它所继承的Customer成员变量副本,而那些成员变量却未被复制。

因此PriorityCustomer对象的Customer成分会被不带实参的Customer构造函数(即default构造函数)初始化。

此时,你应该让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 constructor");
    Customer::operator=(rhs);        //对base class成分进行赋值动作
    priority = rhs.priority;
    return *rhs;
}

现在应该很清楚了。当你编写一个copying函数,请确保(1)复制所有local成员变量,(2)调用所有base classes内的适当的copying函数

如果你发现你的copy构造函数和copy assigment操作符有相近的代码,你想到的不应该是令某个copying函数调用另一个copying函数,而是建立一个新的成员函数给两者调用。这样的函数往往是private而且常被命名为init。

总结:

  • Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”
  • 不要尝试以某个copying函数实现另一个copying函数。应该讲共同代码放进第三个函数中,并由两个copying函数共同调用

编于03/25/2019 18:47

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值