最近开始看《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:
- 拷贝函数应确保复制“对象内的所有成员变量”及“所有基类成分”
- 不要尝试以某个拷贝函数实现另一个拷贝函数。应将共同机能放进第三方函数中,并由两个拷贝函数共同调用
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)