前言
标题一看似乎是非常直观简单,没必要特别写下一篇随便记录。
然而,在实际开发中,你会发现做到这一点对于经验不足的 C++ 程序员来说绝对是一个挑战。
要做到复制每一个成分,就一定小心下面说到的两点。
第一点
类中新增了成员变量之后,需要在自定义的构造函数,析构函数,赋值运算符重载函数,拷贝构造函数中做出相应调整。
请看下面一个例子:
1 // 记录调用信息 2 void logCall(const std::string & funcName); 3 4 // 顾客类 5 class Customer { 6 public: 7 //...... 8 Customer(const Customer & rhs); // 自定义拷贝构造函数 9 Customer & operator=(const Customer & rhs); // 自定义赋值运算符 10 //...... 11 private: 12 std::string name; 13 }; 14 15 Customer::Customer(const Customer & rhs) 16 : name(rhs.name) 17 { 18 logCall("Customer copy constructor"); 19 } 20 21 Customer & Customer::operator=(const Customer &rhs) { 22 logCall("Customer copy assignment operator"); 23 name = rhs.name; 24 return *this; 25 }
这段代码定义了一个顾客类,每次调用它的拷贝构造函数以及赋值运算符重载函数就会记录下这次操作。
这里看起来一切都很美好,也确实是这样的。
但当你往这个类中添加了一个成员变量,不小心的话就会出问题了:
1 class Data { /*......*/ }; 2 // 顾客类 3 class Customer { 4 public: 5 //...... 6 Customer(const Customer & rhs); // 自定义拷贝构造函数 7 Customer & operator=(const Customer & rhs); // 自定义赋值运算符 8 //...... 9 private: 10 std::string name; 11 Data lastTransaction; // 看这里! 定义了一个新的变量 12 13 }; 14 15 Customer::Customer(const Customer & rhs) 16 : name(rhs.name) 17 { 18 logCall("Customer copy constructor"); 19 } 20 21 Customer & Customer::operator=(const Customer &rhs) { 22 logCall("Customer copy assignment operator"); 23 name = rhs.name; 24 return *this; 25 }
这里,你必须要对自定义的构造函数,拷贝构造函数,赋值运算符重载函数都做出相应的修改才行。
第二点
子类对象的自定义赋值运算符重载函数,拷贝构造函数要写入父类成员拷贝的相关语句。
还是以上面的实例来进行分析,这里我设计了一个 PriorityCustomer 类继承 Customer 类,如果要自定义它的拷贝构造函数和赋值运算符,请问这样做对吗?
1 class PriorityCustomer : public Customer { 2 public: 3 //...... 4 PriorityCustomer(const PriorityCustomer & rhs); // 自定义拷贝构造函数 5 PriorityCustomer & operator=(const PriorityCustomer & rhs); // 自定义赋值运算符 6 //...... 7 private: 8 int priority; 9 }; 10 11 PriorityCustomer::PriorityCustomer(const PriorityCustomer & rhs) 12 : priority(rhs.priority) 13 { 14 logCall("PriorityCustomer copy constructor"); 15 } 16 17 PriorityCustomer & PriorityCustomer::operator=(const PriorityCustomer &rhs) { 18 logCall("PriorityCustomer copy assignment operator"); 19 priority = rhs.priority; 20 return *this; 21 }
答案是否定的。因为这两个函数中,只拷贝了子类对象新定义成员,它们继承自父类部分却没有拷贝。
小结
再说一点就是,如果你发现自定义的拷贝构造函数和赋值运算符重载函数有代码重复,那么最好的做法是建立一个新的函数让它们来调用;不要让拷贝构造函数和赋值运算符重载函数相互调用,这是行不通的。