栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

《Effective C++》学习笔记(条款12:复制对象时勿忘其每一个成分)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Effective C++》学习笔记(条款12:复制对象时勿忘其每一个成分)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!

C++中有两个函数负责对象拷贝,分别是拷贝构造函数和拷贝赋值运算符,我们可以统称为拷贝函数。若我们不声明自己的拷贝函数,则编译器会给你提供,若是声明了自己定义的拷贝函数,必须把所有成员变量拷贝。

void logCall(const std::string& funcName);
class Customer{
public:
    ...
    Customer(const Customer& rhs);
    Customer& operator=(const Customer& rhs);
private:
    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;       //返回*this
}

上述代码中自己声明的拷贝函数都很不错,直到另一个成员变量的出现

class Date {...};
class Customer{
public:
    ...
    Customer(const Customer& rhs);
    Customer& operator=(const Customer& rhs);
private:
    string name;
    Date lastTransaction;	//新出现的成员变量
};

这时候上述的拷贝函数只是局部拷贝,它们的确复制了顾客的 name,但没有赋值新出现的成员变量 lastTransaction。大多数编译器不会因此而发出错误信息,即你的拷贝函数不完整,它不会告诉你。总的来说,你每添加一个成员变量,你就必须修改已有的拷贝函数(也需要修改 class 的所有搞糟函数以及任何非标准形式的 operator=)。

一旦发生继承呢?假设 PriorityCustomer 类继承了 Customer 类,如下述代码:

class PriorityCustomer : public Customer{
public:
    ...
    PriorityCustomer(const PriorityCustomer& rhs);
    PriorityCustomer& operator=(const PriorityCustomer& rhs);
private;
    int priority;
};

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
  	:priority(rhs.prority){           //使用初始化列表来构造该类的数据成员
  	logCall("PriorityCustomer copy constructor");  
}

PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs){
  	logCall("PriorityCustomer copy assignment operator");
	priority = rhs.priority;          //拷贝该类的数据成员
	return *this;
}

PriorityCustomer 的拷贝函数确实完整了复制 PriorityCustomer 中的专属成分,但它的基类 Customer 中的专属成分却未被复制。PriorityCustomer 的拷贝构造函数并没有指定实参传给其基类的构造函数(即它再它的初始化列表中没有提到 Customer ),因此 PriorityCustomer 中其基类的专属成分会被无参构造函数(必须有一个无参构造函数,不然无法通过编译,因为你定义了拷贝函数,所以编译器不会给你提供默认构造函数)初始化。

解决方法:

//拷贝构造函数:在初始化列表上调用其基类的拷贝函数
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
  	:Customer(rhs),			//调用基类的拷贝构造函数
	priority(rhs.prority){
  	logCall("PriorityCustomer copy constructor");  
}

//拷贝赋值运算符:对基类成分进行赋值操作
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs){
  	logCall("PriorityCustomer copy assignment operator");
	Customer::operator=(rhs);	//对基类成分进行赋值操作
    priority = rhs.priority;
	return *this;
}

所以,当自己编写一个拷贝函数时,请确保:

  • 复制所有本地成员变量
  • 调用所有基类内的适当的拷贝函数

注意:在拷贝赋值运算符函数中调用拷贝构造函数是不合理的,反之,在拷贝构造函数中调用拷贝赋值运算符同样是无意义的。

Note:

  • 拷贝函数应确保复制“对象内的所有成员变量”及“所有基类成分”
  • 不要尝试以某个拷贝函数实现另一个拷贝函数。应将共同机能放进第三方函数中,并由两个拷贝函数共同调用
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/433448.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号