C++空类默认函数
定義一個(gè)空的C++類(lèi),例如
class Empty
{
}
一個(gè)空的class在C++編譯器處理過(guò)后就不再為空,編譯器會(huì)自動(dòng)地為我們聲明一些member function,一般編譯過(guò)去就相當(dāng)于
class Empty
{
public:
Empty(); // 缺省構(gòu)造函數(shù)
Empty( const Empty& ); // 拷貝構(gòu)造函數(shù)
~Empty(); // 析構(gòu)函數(shù)
Empty& operator=( const Empty& ); // 賦值運(yùn)算符
Empty* operator&(); // 取址運(yùn)算符
const Empty* operator&() const; // 取址運(yùn)算符 const
};
一般的書(shū)上好像都是前面四種:默認(rèn)構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),默認(rèn)賦值函數(shù)以及析構(gòu)函數(shù),后面兩種其實(shí)屬于,但要需要注意的是,只有當(dāng)你需要用到這些函數(shù)的時(shí)候,編譯器才會(huì)去定義它們。
如果你只是聲明一個(gè)空類(lèi),不做任何事情的話,編譯器會(huì)自動(dòng)為你生成一個(gè)默認(rèn)構(gòu)造函數(shù)、一個(gè)拷貝默認(rèn)構(gòu)造函數(shù)、一個(gè)默認(rèn)拷貝賦值操作符和一個(gè)默認(rèn)析構(gòu)函數(shù)。這些函數(shù)只有在第一次被調(diào)用時(shí),才會(huì)別編譯器創(chuàng)建。所有這些函數(shù)都是inline和public的。
默認(rèn)的析構(gòu)函數(shù)是非虛函數(shù)(除非基類(lèi)有自己聲明的虛析構(gòu)函數(shù))。而拷貝默認(rèn)構(gòu)造函數(shù)和默認(rèn)拷貝賦值操作符知識(shí)是單純將來(lái)源對(duì)象的每一個(gè)非靜態(tài)成員拷貝到對(duì)象目標(biāo)中(bitwise copy)。
其中的默認(rèn)拷貝賦值操作符只有在生成的代碼合法并且有機(jī)會(huì)證明它有意義存在時(shí)才會(huì)生成。這就說(shuō)明,如果你打算在一個(gè)“內(nèi)含引用成員”或者“內(nèi)含const成員”的類(lèi)內(nèi)支持賦值操作,就必須定義自己的默認(rèn)拷貝賦值操作符。因?yàn)镃++本身不允許引用改指不同的對(duì)象,也不允許更改const成員。
最后一種情況,當(dāng)基類(lèi)將自己的默認(rèn)拷貝賦值操作符聲明為private時(shí),子類(lèi)就不會(huì)產(chǎn)生自己的的默認(rèn)拷貝賦值操作符。因?yàn)榧偃绠a(chǎn)生了這樣的默認(rèn)拷貝賦值操作符,它會(huì)試著去調(diào)用基類(lèi)的默認(rèn)拷貝賦值操作符去處理基類(lèi)的部分,不幸的是,它沒(méi)有權(quán)利。
你可以將拷貝構(gòu)造函數(shù)或默認(rèn)拷貝賦值操作符聲明為private。這樣明確聲明一個(gè)成員函數(shù),就阻止了編譯器暗自創(chuàng)建的默認(rèn)版本,而這些函數(shù)為private,使得可以成功阻止人們調(diào)用它。
上面的做法有一個(gè)隱患,因?yàn)轭?lèi)自身的member和friend還是可以調(diào)用這些private函數(shù)。有一個(gè)很刁鉆的方法,“將成員函數(shù)聲明為private而且故意不實(shí)現(xiàn)它們”,這樣既阻止了默認(rèn)函數(shù)的生成,而且如果你試著調(diào)用這些函數(shù),就會(huì)得到一個(gè)鏈接錯(cuò)誤。只聲明,不定義,鏈接器報(bào)錯(cuò)。甚至在聲明的時(shí)候,你連參數(shù)也不用寫(xiě)。
而試著將上述的鏈接器錯(cuò)誤提前到編譯器也是可以的。我們專(zhuān)門(mén)設(shè)計(jì)一個(gè)類(lèi)Unconpyable。
--------------------------------------------------------------------
class Uncopybale {
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Ucopyable(const Uncopyable&)
Uncopyable& operator=(const Uncopyable&)
};
--------------------------------------------------------------------
為了阻止對(duì)象被拷貝,我們唯一需要做的就是繼承Uncopyable。這些函數(shù)的默認(rèn)生成版本會(huì)嘗試調(diào)用其基類(lèi)的對(duì)應(yīng)版本,那些調(diào)用會(huì)被編譯器拒絕,因?yàn)樗?lèi)的拷貝函數(shù)是private。
Boost提供的noncopyable類(lèi)也有類(lèi)似的功能。
忠告:
為了駁回編譯器自動(dòng)提供的技能,可將相應(yīng)的成員函數(shù)聲明為private并且不予實(shí)現(xiàn)。使用像Uncopyable這樣的基類(lèi)也是一種做法。
C++】使用對(duì)象前請(qǐng)先正確初始化 ——《Effective C++》讀書(shū)筆記3
2009-03-15 06:28
并不是所有的編譯器都包成對(duì)象的內(nèi)置類(lèi)型成員會(huì)被自動(dòng)初始化為0。永遠(yuǎn)在使用對(duì)象之前先將它初始化。確保每一個(gè)構(gòu)造函數(shù)都將對(duì)象的每一個(gè)成員初始化。
別把賦值錯(cuò)當(dāng)成初始化。C++規(guī)定,對(duì)象的成員變量的初始化動(dòng)作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前(對(duì)于內(nèi)置類(lèi)型對(duì)象可能不確定),這點(diǎn)對(duì)于非內(nèi)置類(lèi)型對(duì)象來(lái)說(shuō)尤其關(guān)鍵。如果你沒(méi)有在成員初始化列表(member initialization list)為其初始化,它們將調(diào)用自己的默認(rèn)構(gòu)造函數(shù),然后才進(jìn)入構(gòu)造函數(shù)內(nèi)部(很可能你會(huì)在這里給他們賦值)。在成員初始化列表中的初始化只是調(diào)用了拷貝構(gòu)造函數(shù)一次,而在構(gòu)造函數(shù)內(nèi)部再為其賦值則在調(diào)用默認(rèn)構(gòu)造函數(shù)后又調(diào)用了一次拷貝構(gòu)造函數(shù)。哪個(gè)效率高你當(dāng)然知道。
所以,請(qǐng)用成員初始化列表進(jìn)行初始化,雖然效率提高只針對(duì)于非內(nèi)置類(lèi)型成員,但是規(guī)定總是在初值列中雷楚所有成員變量,這樣就省的有些未被列出的內(nèi)置類(lèi)型成員被忘記初始化。而有些時(shí)候,即使成員變量是內(nèi)置類(lèi)型,也必須要用成員初始化列表(成員變量為const或者reference,它們一定要有初值,而且不能被賦值)。
總之,總是使用成員初始化列表,這樣或者必要,或者高效。有個(gè)例外,當(dāng)你重載多個(gè)構(gòu)造函數(shù),每個(gè)構(gòu)造函數(shù)有很多成員變量和基類(lèi)的時(shí)候(這意味這成員初始化列表會(huì)很多、很長(zhǎng)而且重復(fù)較多),可以將一些內(nèi)置類(lèi)型變量的初始化動(dòng)作(它們的賦值和初始化不影響效率)移到一個(gè)私有函數(shù)中,供所有的構(gòu)造函數(shù)調(diào)用。
規(guī)定:初始化順序是基類(lèi)早于派生類(lèi),類(lèi)成員變量則以其聲明順序?yàn)闇?zhǔn)。所以成員初始化列表中列出的各個(gè)成員的順序最好與聲明的順序相同。
最后說(shuō)個(gè)不常見(jiàn)的問(wèn)題:某個(gè)對(duì)象A的非靜態(tài)成員變量初始化動(dòng)作正好使用了另外一個(gè)編譯單元(另外一個(gè)cpp)中的某個(gè)非靜態(tài)對(duì)象B,你不能保證A在需要B的時(shí)候,B就已經(jīng)被編譯好而且產(chǎn)生了。解決的辦法是將對(duì)象A和對(duì)象B都分別放到函數(shù)中(貌似是專(zhuān)門(mén)為每個(gè)這樣的對(duì)象定制的對(duì)象),并且聲明為static。這些函數(shù)返回的是靜態(tài)對(duì)象的引用。這是單例模式的一種實(shí)現(xiàn)。在程序中以前需要對(duì)象引用的地方直接調(diào)用這些函數(shù)就好了。這種reference-returning函數(shù)對(duì)于處理多線程環(huán)境下的“競(jìng)速形勢(shì)(race conditions”的方法是:在程序的單線程啟動(dòng)階段手工調(diào)用所有的reference-returning函數(shù)。
忠告:
1 為內(nèi)置對(duì)象進(jìn)行手工初始化,因?yàn)镃++不保證初始化它們。
2 構(gòu)造函數(shù)最好使用成員初始化列表(member initialization list),而不要在構(gòu)造函數(shù)本體內(nèi)使用賦值操作。初始化列表列出的成員變量,其排列次序應(yīng)該和它們?cè)赾lass中的聲明次序相同。
3 為免除“跨編譯單元的初始化次序”問(wèn)題,請(qǐng)以local static對(duì)象替換non-local static對(duì)象。
總結(jié)
- 上一篇: 我用的archlinux+slim+op
- 下一篇: Hadoop的Python语言封装