就語言而言,個人還是比較喜歡C++,盡管 C++有些語法方面確實(shí)比較深奧,但這些確實(shí)擋不住它在實(shí)際應(yīng)用中不可被替代的位置。
成都創(chuàng)新互聯(lián)公司始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營理念,通過多達(dá)10多年累計(jì)超上千家客戶的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的全網(wǎng)營銷推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶,其中包括:自拌料攪拌車等企業(yè),備受客戶贊揚(yáng)。
開始談今天的重點(diǎn),如何定義一個特殊的C++類。
1、定義不可被繼承的C++類
如何讓一個類不能被繼承呢?簡單來說,我們希望達(dá)到的效果,就是如果繼承這個類的話,編譯直接報(bào)錯。
實(shí)現(xiàn)這個類,我希望你提前了解過以下幾個C++的簡單語法:友元類、虛繼承。這里我直接告訴你如何來定義,接下來我們討論為什么。
第一步:定義一個空類A,顯式給出構(gòu)造和析構(gòu),構(gòu)造和析構(gòu)必須定義為private。
第二步:定義不可被繼承的類B,給出正常的構(gòu)造和析構(gòu),public類型,讓 B虛繼承A類,繼承方式不限,同時將B設(shè)置為A的友元類。
第三步:定義類C去嘗試虛擬繼承類B,編譯錯誤!
代碼如下:
class A { friend class B; private: A(){} ~A(){} }; class B:virtual public A { public: B() {} }; class C :public B { public: C() {} };
我們來討論,為什么編譯會出錯?
首先,將類A的構(gòu)造函數(shù)和析構(gòu)函數(shù)定義為私有,如果沒有顯示定義,默認(rèn)的構(gòu)造和析構(gòu)函數(shù)是public的。那定義為protected類型可以嗎?答案是“NO”。了解過繼承的話,應(yīng)該知道,基類中private類型的成員變量或方法,在所有派生類中是不可見,但protected類型的成員變量或方法是可見的。我們的目的是如果以后構(gòu)造類A的派生類時,需要調(diào)用類A構(gòu)造,必須在類A內(nèi)部調(diào)用該方法,即使是派生類,我也希望它遵守,即對派生類不可見。
其次,類B繼承了類A,繼承方式無所謂,因?yàn)榛愃接胁糠植徽撘院畏N方式繼承,都是不可見的。為什么要虛繼承呢?這個稍后說。同時,類B這里定義成類A的友元類,那么以后類B就可以直接訪問類A的私有成員了,也就是說,這里的友元,打破了我們一開始定義的對派生類不可見的限制。那么以后在使用類B實(shí)例化對象時,完全可以成功,構(gòu)造基類部分是通過友元實(shí)現(xiàn)的,和繼承方式無關(guān)。這里的類B就是我們定義的不可被繼承的類。
最后,我們嘗試讓類C繼承類B,那么當(dāng)類C實(shí)例化對象時(或顯式定義了構(gòu)造函數(shù)),就會首先去構(gòu)造屬于類B的部分。這里注意,如果類B不是虛繼承類A的話,那么這里構(gòu)造屬于類B的部分時,是通過類B構(gòu)造類A部分,這必然是成功的。但我們不希望,因此,這里類B虛繼承了類A,當(dāng)構(gòu)造屬于類B部分時,由于B虛繼承了A,那么會由類C直接去訪問A,嘗試構(gòu)造類A的部分,很明顯,由于訪問不到類A的構(gòu)造函數(shù),因此C實(shí)例化對象失敗。之后,所有嘗試?yán)^承類B的類都會在編譯時失敗(前提要求C顯示給出了構(gòu)造,或類C實(shí)例化了對象)。
到這里,不可被繼承了類B創(chuàng)建成功,這里很巧妙的應(yīng)用了友元類的概念,從而實(shí)現(xiàn)了僅有B可以實(shí)例化屬于A的部分。
如果覺得這種方式太過靈活,不容易理解,那么還有一種更加簡單的方式。C++11引入了final關(guān)鍵字,被該關(guān)鍵字修飾的類,都是不可被繼承的,這個和java中的用法基本是一致的。
class A final { public: A(){} }; class B :public A // 編譯出錯 {};
2、定義一個只可以在棧上創(chuàng)建對象的類
如果上面那種情況理解的話,這里應(yīng)該不會太難。為什么只可以在棧上創(chuàng)建對象?如何實(shí)現(xiàn)?其實(shí)很簡單,只要我們只對外暴露出可以在堆上創(chuàng)建對象的接口就可以。代碼如下:
class A { public: static A* Get_A(int x) { return new A(x); } static void Delete_A(A* a) { delete a; } private: A(int a = 10) :_a(a) {} private: int _a; }; int main() { A* pa = A::Get_A(9); A* pb = A::Get_A(7); return 0; }
和之前一樣,將構(gòu)造函數(shù)和析構(gòu)函數(shù)都設(shè)置為私有(因?yàn)檫@里不涉及繼承,private和protected在這里沒有區(qū)別),由于構(gòu)造函數(shù)都是私有的,無法創(chuàng)建對象,因此,提供了靜態(tài)方法,該方法中通過new和delete實(shí)現(xiàn)了在堆上對象的獲取和釋放。
3、定義一個只可以在棧上創(chuàng)建對象的類
如果理解了上一種,就應(yīng)該知道這里該如何去做。只要我們只提供在棧上獲取對象的方式即可,由于??臻g是由操作系統(tǒng)維護(hù)了,沒有特殊需要,析構(gòu)函數(shù)就沒有必要顯式給出,代碼如下:
class A { public: static A Get_A(int x) { return A(x); } private: A(int a = 10) :_a(a) {} private: int _a; }; int main() { A pa = A::Get_A(9); A pb = A::Get_A(7); return 0; }
只能在棧上或者只能在堆上創(chuàng)建的對象,實(shí)現(xiàn)起來原理是一樣的,類的構(gòu)造和析構(gòu)函數(shù)都設(shè)置為私有,當(dāng)我的接口函數(shù)提供的方法是從堆中創(chuàng)建的對象時,類就只能在堆上創(chuàng)建對象,當(dāng)我的接口函數(shù)提供的是從棧上直接得到的對象的話,類就只可以在棧上創(chuàng)建對象。需要注意一點(diǎn)的是,創(chuàng)建棧上對象的時候,不可以返回臨時對象的引用,這個就不再多解釋。
------muhuizz整理