C++要實現一個不能被繼承的類有不少方法.使用友元、私有構造函數、虛繼承等方式可使一個類不能被繼承,但是爲何必須是虛繼承?背後的原理又是什麼?ios
~的構造函數設置爲私有的就okay。面試
由於那樣的話,子類就沒有辦法訪問基類的構造函數,從而就阻止了進行子類構造對象的任務實現,也就達到了不可繼承的目的。函數
可是,假設那樣,這個類咱們在其它地方怎麼使用呢?那這樣子給咱們的利用也形成了必定的障礙。測試
好了。你是否是也想到了,定義靜態方法,在方法內部實現一個對象,而後返回它的指針。spa
Ok?那怎麼釋放掉呢?再照樣設計一個釋放內存函數,問題就會迎刃而解。設計
OK。按照這個邏輯分析。示例代碼以下:
指針
點擊(此處)摺疊或打開code
好了,這個類就這樣子。按照咱們的理論分析,咱們的實踐結果是徹底成立的。對象
可是,這個題,它比較有挑戰性,什麼意思呢?難道你沒有發現,我們這水平也就僅僅有面試資格,還不能夠破格錄用的。繼承
怎麼啦?你可能會反問我。難道你真的沒有看明白?肯定沒有看明白?若是是真的話,那我就告訴你吧!
我們的類不能夠實如今棧上建立對象。也就是說,僅僅只能夠在堆上構建任何的一個對象,而在棧上就無能爲力了。
私有的構造函數極大的侷限性就這樣盡收眼底了。
好吧!咱們修改它,也就是所謂的爲它打「補丁吧」,請看示例代碼:
點擊(此處)摺疊或打開
#include<iostream>
using namespace std;
template <typename T>
class Base
{
friend T;
private:
Base(){}
~Base(){}
};
class Finalclass : virtual public Base<Finalclass>
{
public:
Finalclass(){}
~Finalclass(){}
};
void main()
{
Finalclass *p = new Finalclass; //堆上對象
Finalclass fs; //棧上對象
}
OK。如今看看咱們的Finalclass類。
繼承於Base,Base爲虛基類,由於它是Base的友元,因此,它能夠訪問基類的私有構造函數,以及析構函數。編譯運行時是正確的。
也就是說,能夠建立堆上的對象,而且能夠構建棧上的對象。
能否繼承?假如它做爲一個基類被一個類繼承,在編譯時是徹底能夠經過的。
這一點沒有什麼疑問,問題就出在運行時:
當子類在構造對象時,由於是虛繼承,因此子類的構造函數會直接去調用Base類的構造函數,而Base的構造函數是私有的。運行錯誤error!!!
這就是一個真正不能被繼承的類。
思路二:主要的思路就是使子類不能構造父類的部分,這樣子類就沒有辦法實例化整個子類.這樣就限制了子類的繼承. 因此咱們能夠將父類的構造函數聲明成爲私有的,可是這樣父類不就不能實例化,繼續思考、、、
咱們能夠利用友員不能被繼承的特性!
首先假設CParent不可以被繼承. 讓CParent是某一個類的友員和子類,CParent能夠構造,可是CParent的子類 CChild卻不能繼承那個友員特性,因此不能被構造.因此咱們引入一個CFinalClassMixin.
咱們對這個類的功能是這麼指望的:
任何類從它繼承都不能被實例化,同時這個類自己咱們也不但願它被實例化.
實現一個構造函數和析構函數都是private的類就好了.同時在這類裏面將咱們的CParent聲明爲友員. 代碼以下:
class CFinalClassMixin
{
friend class CParent;
private:
CFinalClassMixin(){}
~CFinalClassMixin(){}
};
//咱們的 CParent代碼應該以下:
class CParent
{
public:
CParent(){}
~CParent(){}
};
這個類(注,此時它仍是可以被繼承),如今咱們須要它不能被繼承.那麼只要將代碼改爲
class CParent:public CFinalClassMixin
{
public:
CParent(){}
~CParent(){}
};
就好了.如今從CParent繼承一個子類試試
class CChild:public CParent{};
編譯一下代碼試試,發現:居然沒有做用!!
如今再回想一下咱們這麼操做的緣由,也就是這個方案的原理,那就是讓父類能夠訪問Mixin類的構造函數,可是子類不能訪問.
如今看看咱們的代碼,發現父類是CFinalClassMixin類的友員,能夠訪問它的構造函數.由於友員不能繼承,因此CChild不能訪問CFinalClassMixin的構造函數.因此應該不能被實例化.
CChild的確不能訪問 CFinalClassMixin的構造函數,可是它卻沒必要調用它!我想這就是問題的緣由所在.CChild是經過CParent來構造 CFinalClassMixin的,因此這個友員對他並無什麼用處!
如今問題找到了.要解決很簡單.只要讓CChild必須調用 CFinalClassMixin的構造函數就好了,怎麼才能達到目的呢?
還記得虛繼承嗎?虛繼承的一個特徵就是虛基類的構造函數由最終子類負責構造!因此將CParent從CFinalClassMixin繼承改爲從CFinalClassMixin虛繼承就能夠了.代碼以下:
class CParent:virtual public CFinalClassMixin
{
public:
CParent(){}
CParent(){}
};
如今試試,行了.
可是可能有些人會對多繼承心有餘悸!可是咱們這裏並無必要這麼擔憂!爲何?由於咱們的CFinalClassMixin類是純的!pure! 也就是說它根本沒有成員變量!那麼咱們就根本不用擔憂多繼承帶來的最大問題.菱形繼承產生的數據冗餘.以及二義性.
如今還有個不足!那就是 咱們不能每次使用這個CFinalClassMixin類就在裏面加上對某個類的友員聲明啊!這多麻煩啊!雖然不是什麼大問題,可是我覺的仍是要解決,由於我充分信任C++!
解決的方法也很簡單!那就是使用模板!具體描述就省略了,給出代碼你們一看就知道了
下面是個人測試程序的完整代碼(其中的CFinalClassmixin已經改爲模板)
#include "stdafx.h"
#include <iostream>
using namespace std;
template<class T> //應用模板
class CFinalClassMixin
{
friend T;
private:
CFinalClassMixin(){}
~CFinalClassMixin(){}
};
class CParent:virtual public CFinalClassMixin<CParent> //虛繼承
{
public:
CParent(){}
~CParent(){}
};
class CChild:public CParent{}; //子類繼承父類
int main(int argc, char* argv[])
{
CParent a; // 能夠構造
CChild b; //不能構造
return 0;
}
如今只要對不想被繼承的類加入一個CFinalClassMixin混合類作父類就好了.
經過限制構造函數,咱們就達到了限制繼承的目的 .可是這對有些仍是個例外,好比全是靜態函數的類.這些類自己就不須要構造. 因此咱們對它沒有辦法.可是在大多數狀況下,一個全是靜態函數的類多少暗示了程序自己的設計多是須要斟酌的.
其實這只是Mixin類(混合類)使用的一個小小例子.還有不少其餘的用處,好比UnCopiale等等.就很少說了. 我想說明的是你們可能對多繼承比較反感.可是過度否認也是得不償失的.如今對多繼承到底應不該該使用還處在爭論階段. 我以爲一個方法是否使用得當,關鍵仍是在於使用的人.