有時候,進行類體設計時,會發現某個類的對象是獨一無二的,沒有完全相同的對象,也就是對該類對象做副本沒有任何意義.
因此,需要限制編譯器自動生動的拷貝構造函數和賦值構造函數.一般參用下面的宏定義的方式進行限制,代碼如下:
// A macro to disallow the copy constructor and operator= functions // This should be used in the priavte:declarations for a class #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ TypeName& operator=(const TypeName&) class Test { public: Test(int t); ~Test(); private: DISALLOW_COPY_AND_ASSIGN(Test); };
聲明私有的拷貝構造函數和賦值構造函數,但不去定義實現它們,有三方面的作用:
1.聲明了拷貝構造函數和賦值函數,阻止了編譯器暗自創建的專屬版本.
2.聲明了private,阻止了外部對它們的調用.
3.不定義它們,可以保證成員函數和友元函數調用它們時,產生一個連接錯誤.
上述解決方法,面對在成員函數和友元函數企圖拷貝對象時,會產生連接器錯誤.
遵循錯誤發現越早越好的原則,我們希望將連接期錯誤移至編譯期.
解決思路是:設計一個專門為了阻止copying動作(包含copy和assign)而設計的基類
class Uncopyable { protected: Uncopyable() {} ~Uncopyable() {} private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable&); }; class Test:private Uncopyable{ ... //class不再聲明copy構造函數或copy assignment操作符 };
如上述代碼,當任何人(包括member函數或friend函數)嘗試拷貝Test對象時,編譯器便試着生成一個copy構造函數和一個copy assignment操作符.
編譯器自動生成這些函數時,會調用其基類的對應函數,而基類中這些函數是private,因而那些調用會被編譯器拒絕,產生編譯器錯誤.
像Uncopyable這樣的基類,沒有non-static成員變量,沒有virtual函數,也沒有virtual base classes,可以滿足空白基類最優化的條件(empty base optimization).
這類基類的派生類,不會繼承的關系而產生多余的空間存儲.
class Empty {}; class HoldsAnInt:private empty { private: int x; }; sizeof(HoldsAnInt) == sizeof(int)
這種解決方法不足之處是會導致多重繼承,而多重繼承有時會阻止empty base optimization,將會引入多余的空間存儲.
啟發:針對具體的應用,其類的用途和性質要分析清楚,從而選擇更合適的設計方法.
設計的過程,就是一個權衡的過程,有時候這種方法好,有時候另一種方法好,需要與具本應用結合,從而折中選擇.
參考資料:Effective C++