条款12:牢记复制对象的所有成员

考虑一个class用来表示顾客,我们自己实现copying函数而非由编译器提供(注:编译器对于copying函数的默认实现就是将对象的所有成员变量都复制一份):

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 void logCall(const string& funcStr)
 6 {
 7     cout << funcStr << endl;
 8 }
 9 
10 class Customer
11 {
12 public:
13     Customer(){ }
14     Customer(const Customer& rhs);
15     Customer& operator=(const Customer& rhs);
16 
17 private:
18     string name;
19 };
20 Customer::Customer(const Customer& rhs)
21 {
22     logCall("调用Customer类的复制构造函数");
23 }
24 Customer& Customer::operator=(const Customer& rhs)
25 {
26     logCall("调用Customer类的赋值操作符函数");
27     if(this == &rhs)
28         return *this;
29     name = rhs.name;
30     return *this;
31 }
32 
33 int main()
34 {
35     Customer c1;
36     Customer c2(c1);    // 调用复制构造函数
37     c1 = c2;            // 调用赋值操作符函数
38 
39     return 0;
40 }

上边程序运行正常,但是如果现在向Customer类中加入一个变量:

 1 class Customer
 2 {
 3 public:
 4     Customer(){ }
 5     Customer(const Customer& rhs);
 6     Customer& operator=(const Customer& rhs);
 7 
 8 private:
 9     string name;
10     double cost;
11 };

此时必须重写copying函数,将cost变量加入。尤其是当有继承关系时,如果派生类要重新定义copying函数,必须考虑到基类中的变量也要发生copying,这点非常重要,而且由于在基类中变量一般为private,你不能直接copy它们,那么可以通过调用基类的copying函数来处理,如下所示:

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 void logCall(const string& funcStr)
 6 {
 7     cout << funcStr << endl;
 8 }
 9 
10 class Customer
11 {
12 public:
13     Customer(){ }
14     Customer(const Customer& rhs);
15     Customer& operator=(const Customer& rhs);
16 
17 private:
18     string name;
19 };
20 Customer::Customer(const Customer& rhs)
21 {
22     logCall("调用Customer类的复制构造函数");
23 }
24 Customer& Customer::operator=(const Customer& rhs)
25 {
26     logCall("调用Customer类的赋值操作符函数");
27     if(this == &rhs)
28         return *this;
29     name = rhs.name;
30     return *this;
31 }
32 
33 class PriorityCustomer : Customer
34 {
35 public:
36     PriorityCustomer(){ }
37     PriorityCustomer(const PriorityCustomer& rhs);
38     PriorityCustomer& operator=(const PriorityCustomer& rhs);
39 
40 private:
41     int priority;
42 };
43 PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs) : Customer(rhs),  // 调用基类的复制构造函数
44     priority(rhs.priority)
45 {
46     logCall("调用派生类PriorityCustomer的复制构造函数");
47 }
48 PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
49 {
50     logCall("调用派生类PriorityCustomer的赋值操作符函数");
51     Customer::operator=(rhs);    // 对基类成员赋值
52     priority = rhs.priority;
53     return *this;
54 }
55 int main()
56 {
57     PriorityCustomer pc1;
58     PriorityCustomer pc2(pc1);    // 基类和派生类的copy构造函数都会得到调用
59     pc1 = pc2;                    // 基类和派生类的copy assignment操作符函数都会得到调用
60 
61     return 0;
62 }

实际中,这两个copying函数往往有相同的实现代码,那么能否在copy构造函数(1)中调用copy assignment操作符函数(2)来实现代码复用呢?

答案是永远不要这样做,反过来调用也不行,解释如下:

我们都知道复制构造函数的作用是用一个已有的对象构造另一个不存在的对象,而赋值操作符函数的作用是用一个已有的对象对另一个已经存在的对象进行赋值

如果在(1)中调用(2),相当于将一个已有的对象赋值给一个不存在的对象,自然是不行的;反过来,在(2)中调用(1),相当于在构造一个已经存在的对象了,仍然不行,因此,二者绝不能相互调用。如果确实有相当多的代码是重复使用的,可以将其写入一个公共函数内,让两个copy函数分别调用它,一般而言,这个函数可能被命名为init,并且是private的。

 

总结:

要保证自定义的copying函数复制了<对象内的所有成员变量>及其<基类中的所有变量>。

不要尝试让一个copying函数调用另一个copying函数,应该将其公共代码放入一个第三方函数中,让两个copying函数分别调用它。

原文地址:https://www.cnblogs.com/benxintuzi/p/4530721.html