如果想讓你的類定義出來的對(duì)象是獨(dú)一無二的,即對(duì)象無法被復(fù)制,或者使用賦值操作符賦給另外一個(gè)對(duì)象,那么最好的方法就是禁用拷貝構(gòu)造函數(shù)和賦值操作符。下面介紹幾種禁用的方法。(方法來自Effective C++,如果想禁用類的其他函數(shù),方法類似)
1. 定義為private且不實(shí)現(xiàn)它
我們知道,拷貝構(gòu)造函數(shù)和賦值操作符重載函數(shù),即使不定義,編譯器也會(huì)生成一個(gè)默認(rèn)的函數(shù)。但是如果定義了,class還是會(huì)支持這兩個(gè)函數(shù)。那么應(yīng)該怎么去禁用它們呢?
在C++中,如果不想讓對(duì)象調(diào)用某個(gè)方法,那么可以將這個(gè)方法聲明為private,因此,我們可以將拷貝構(gòu)造函數(shù)和賦值操作符重載函數(shù)聲明為private,就可以阻止別人調(diào)用它了。但是,private函數(shù)還是可以在類的成員函數(shù)和friend函數(shù)中調(diào)用。這里還有一個(gè)技巧,就是將函數(shù)定義為private而且不實(shí)現(xiàn)它。
class SpecialClass { ... private: SpecialClass(const SpecialClass&); SpecialClass& operator=(const SpecialClass&); };
將SpecialClass這樣定義,并且不實(shí)現(xiàn)這兩個(gè)函數(shù),那么如果有人試圖拷貝這個(gè)類的對(duì)象,編譯器就會(huì)報(bào)錯(cuò):
SpecialClass sc1; SpecialClass sc2(sc1); //error, 編譯出錯(cuò) SpecialClass sc3; sc3 = sc1; //error, 編譯出錯(cuò)
而且即使在成員函數(shù)或者friend函數(shù)中調(diào)用了它,鏈接時(shí)也會(huì)出錯(cuò)。
2. 繼承Uncopyable類
第一種方法可以解決禁用拷貝構(gòu)造函數(shù)的問題,但是還有一點(diǎn)不完美的是,如果用戶不小心在成員函數(shù)或者friend函數(shù)中調(diào)用了它,那么這個(gè)錯(cuò)誤只有到鏈接階段才能被發(fā)現(xiàn)。所以就有了繼承一個(gè)Uncopyable類的方法:
class Uncopyable { protected: Uncopyable() {} ~Uncopyable() {} private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable&); };
此時(shí),只需要繼承Uncopyable類就可以防止其對(duì)象被拷貝了。其原理是:SpecialClass繼承了Uncopyable函數(shù),它的對(duì)象如果調(diào)用了拷貝構(gòu)造函數(shù),或者使用了賦值操作符,那么編譯時(shí),就會(huì)去調(diào)用基類的對(duì)應(yīng)函數(shù),但是基類里的這兩個(gè)函數(shù)是private的,所以編譯會(huì)失敗。
另外,boost也提供了一個(gè)Uncopyable函數(shù),那個(gè)class名字為noncopyable,因此我們也可以這樣做:
#include <boost/utility.hpp> class SpecialClass : boost::noncopyable { ... };
3. C++0x中的新方法
在C++0x中,新添加了兩個(gè)關(guān)鍵字:default和delete,可以用于控制某個(gè)類使用默認(rèn)函數(shù)(default)或者禁用某個(gè)函數(shù)(delete)。其中,使用default關(guān)鍵字可以將某個(gè)函數(shù)定義為默認(rèn)函數(shù)。如:
class SpecialClass { ... public: SpecialClass(const SpecialClass&) = default; SpecialClass& operator=(const SpecialClass&) = default; };
可能這樣寫有點(diǎn)多余,但是它可以直觀表示“我使用的默認(rèn)的函數(shù)”。而且,如果有人想顯式定義默認(rèn)函數(shù),但是自己寫的代碼又可能有錯(cuò)誤,就可以使用這個(gè)關(guān)鍵字,方便且不出錯(cuò)。
delete關(guān)鍵字可以禁用掉類的方法,正如我們上面前兩節(jié)所做的事情,就可以用這一個(gè)關(guān)鍵字完成:
class SpecialClass { ... public: SpecialClass(const SpecialClass&) = delete; //禁用拷貝構(gòu)造函數(shù) SpecialClass& operator=(const SpecialClass&) = delete; };
default關(guān)鍵字可以用于一切含有默認(rèn)方法的函數(shù)。delete關(guān)鍵字可以用于類的所有方法,例如可以禁用掉函數(shù)的某些類型的參數(shù):
class SpecialClass { ... public: void fn(long); void fn(int) = delete; }; SpecialClass sc; long l; sc.fn(l); //ok, 調(diào)用void fn(long); int i; sc.fn(i); //error, 調(diào)用void fn(int);
上面的代碼會(huì)將void fn(int)類型的函數(shù)禁用掉,而如果不禁用的話,sc.fn(i)會(huì)進(jìn)行隱式轉(zhuǎn)換調(diào)用void fn(long)函數(shù)。