条項12:複製対象のすべてのメンバーを念頭に置く

14458 ワード

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 }

実際、この2つのcopying関数には同じ実装コードがあることが多いが、copy構築関数(1)でcopy assignmentオペレータ関数(2)を呼び出してコード多重化を実現できるだろうか.
答えは永遠にそうしないでください.逆に呼び出してもだめです.説明は以下の通りです.
レプリケーションコンストラクション関数の役割は、既存のオブジェクトで別の存在しないオブジェクトを構築することであり、付与オペレータ関数の役割は、既存のオブジェクトで別の既存のオブジェクトを付与することであることが知られています.
(1)で(2)を呼び出すと、既存のオブジェクトを存在しないオブジェクトに割り当てることに相当し、当然だめである.逆に,(2)で(1)を呼び出すのは,既存のオブジェクトを構築するのに相当し,依然としてだめであるため,両者は決して互いに呼び出すことができない.確かにかなりのコードが重複して使用されている場合は、2つのcopy関数がそれぞれ呼び出されるように共通関数に書き込むことができます.一般的に、この関数はinitと命名され、privateである可能性があります.
 
まとめ:
カスタムcopying関数が<オブジェクト内のすべてのメンバー変数>とその<ベースクラス内のすべての変数>をコピーしていることを保証します.
1つのcopying関数に別のcopying関数を呼び出すのではなく、共通コードをサードパーティ関数に入れ、2つのcopying関数にそれぞれ呼び出すようにします.