1. 程式人生 > >Effective C++——》條款12:複製物件時勿忘其每一部分

Effective C++——》條款12:複製物件時勿忘其每一部分

這句話包含兩部分的意思:第一部分是要考慮到所有成員變數特別是後加入的,相應的拷貝建構函式和賦值運算子要及時更新;第二部分是在存在繼承時不要遺忘基類部分的複製。先看第一部分的意思,舉個例子:

複製程式碼
1 class SampleClass
2 {
3 private:
4          int a;
5 public:
6          SampleClass(const SampleClass& s):a(s.a)
7          {}
8 };
複製程式碼

這裡只舉了一個拷貝建構函式的例子,賦值運算子與之類似,如果這個時候又加了一個成員變數,比如double b,拷貝建構函式和賦值運算子就要相應地更新(建構函式當然也要更新,只是建構函式一般不會被忘記,而拷貝建構函式和賦值運算子卻常常被遺忘)。像這樣:

複製程式碼
1 class SampleClass
2 {
3 private:
4          int a;
5          double d;
6 public:
7          SampleClass(const SampleClass& s):a(s.a),d(s.d)
8         {}
9 };
複製程式碼

再看第二部分的意思,當存在繼承關係時:

複製程式碼
1 class Derived: public SampleClass
2 {
3 private:
4          int derivedVar;
5 public:
6          Derived(const
Derived& d):derivedVar(d.derivedVar){} 7 };
複製程式碼

像這樣,很容易就會漏掉基類的部分,導致基類部分沒有得到正常的拷貝,應該修改為如下:

複製程式碼
1 class Derived: public SampleClass
2 {
3 private:
4          int derivedVar;
5 public:
6          Derived(const Derived& d):SampleClass(d), derivedVar(d.derivedVar){}
7 };
複製程式碼

對於賦值運算子的過載,應該寫成這樣:

複製程式碼
1 Derived& operator
=(const Derived& d) 2 { 3 SampleClass::operator=(d); 4 derivedVar = d.derivedVar; 5 return *this; 6 }
複製程式碼

可以看到,賦值運算子過載與拷貝建構函式的程式碼具有很高的相似性,但書上說“不要嘗試以某個copying函式實現另一個copying函式”。我覺得這裡有爭議,上一個條款中,書上已經做到了在賦值運算子中呼叫拷貝構造函數了,像這樣:

複製程式碼
1 Derived& operator=(const Derived& d)
2 {
3          Derived tmp(d);
4          swap(*this, tmp);
5          return *this;
6 }
複製程式碼

這就是一個在賦值運算子內呼叫拷貝建構函式的例子,也許在有些情況下,它的效率看上去不那麼高,但卻為程式碼的一致性提供了很好的保障,也能有效提供異常安全性。所以,在這個地方,我不大認同書上所說的,如果你有什麼想法,可以留言共同討論。書上所說的定製一個共有的private函式,比如init(),分別在拷貝建構函式和operator=中呼叫當然也是可以的。

最後總結一下:

Copying函式應該確保複製“物件內的所有成員變數”“所有基類成分”