實驗室同學今天面試,上來被問的第一個題就是”寫一個不能被繼承的類“
這個問題分享出來的時候,我驚呆了,因為腦子沒一點思路(這是什么鬼東西,項目里從來沒有用過,課堂上也沒聽過,在博客、問答區也沒遇見過)。C++里沒有相關的語法定義,也沒有特定的設計。冷靜判斷,覺得這應該就是設計模式一類的東西(這方面確實很欠缺)。不扯了,經過查閱資料,這題是Adobe 07年的面試題,是我太年輕~ 原題是:”寫一個不能被繼承的類,且能正常使用“,難度更大,考查了兩個知識點。
首先,不能被繼承的類,也就是它的子類繼承它后不能正常的實例化。其實,如果能考慮到這里,就很簡單了:子類需要實例化父類,將父類的構造函數設為private,子類就無法實例化了。再進一步,子類的析構函數需要調用父類的析構函數,所以將父類的析構函數設為private也能解決我們的問題。
問題的后半句將難度提升了兩個數量級:且能正常使用。如果將父類的構造函數設為private,它自己就無法正常,它自己的對象沒法正常的聲明、實例化。將構造函數設為了private非常的像單例模式,促使我們想通過friend函數解決無法實例化的問題(當然,如果析構函數是private就是無法釋放,它們同樣的原理,下文不再復述)。
哦,不不,停住,仔細看題目需求,這里要求正常的使用該類,而不是依靠額外的函數途徑去申請一個實例。
這確實是一個難題~如果沒有看過答案,我肯定想不出來。我記得計算機世界有一句名言”一切難題都可以通過增加一個中間層去解決“
要正常的使用該類,它的構造函數和析構函數必然是public訪問權限。我們為它增加一個虛繼承的父類,它是父類的friend(友元),父類的構造函數是private。
直接上代碼:
class A; class final { friend class A;//class關鍵字不可省略,否則在g++中不能編譯通過 final() {} }; class A : virtual public final { public: A()
{}
}; class B : public A { public: B() { } };
class A 是final的friend,所以A可以調用final的構造函數。
因為A虛繼承了final,往后所有繼承A的子類,都必須自己實例化final,以保證final在對象中的唯一性
所以,B繼承A后,需要自己調用虛繼承的父類final的構造函數。顯然,B不是final的友元,不能訪問private中的構造函數。
(注意,將上面的final的構造函數換成了析構函數~final()后,在VS2013中能通過編譯,讀者想想為什么呢?)