教你如何用C++建立一個特殊的類

    就語言而言,我的仍是比較喜歡C++,儘管 C++有些語法方面確實比較深奧,但這些確實擋不住它在實際應用中不可被替代的位置。
java

    開始談今天的重點,如何定義一個特殊的C++類。
ide

一、定義不可被繼承的C++類函數

    如何讓一個類不能被繼承呢?簡單來講,咱們但願達到的效果,就是若是繼承這個類的話,編譯直接報錯。ui

    實現這個類,我但願你提早了解過如下幾個C++的簡單語法:友元類虛繼承這裏我直接告訴你如何來定義,接下來咱們討論爲何。spa

    第一步:定義一個空類A,顯式給出構造和析構構造和析構必須定義爲private。操作系統

    第二步:定義不可被繼承的類B,給出正常的構造和析構,public類型,讓 B虛繼承A類繼承方式不限,同時將B設置爲A的友元類orm

    第三步:定義類C去嘗試虛擬繼承類B,編譯錯誤! 對象

代碼以下:繼承

class A
{
	friend class B;
private:
	A(){}
	~A(){}
};

class B:virtual public A
{
public:
	B()
	{}
};

class C :public B
{
public:
	C()
	{}
};


咱們來討論,爲何編譯會出錯?接口

    首先,將類A的構造函數和析構函數定義爲私有,若是沒有顯示定義,默認的構造和析構函數是public的。那定義爲protected類型能夠嗎?答案是「NO」。瞭解過繼承的話,應該知道,基類中private類型的成員變量或方法,在全部派生類中是不可見,但protected類型的成員變量或方法是可見的。咱們的目的是若是之後構造類A的派生類時,須要調用類A構造,必須在類A內部調用該方法,即便是派生類,我也但願它遵照,即對派生類不可見

    其次,類B繼承了類A,繼承方式無所謂,由於基類私有部分不論以何種方式繼承,都是不可見的。爲何要虛繼承呢?這個稍後說。同時,類B這裏定義成類A的友元類,那麼之後類B就能夠直接訪問類A的私有成員了,也就是說,這裏的友元,打破了咱們一開始定義的對派生類不可見的限制。那麼之後在使用類B實例化對象時,徹底能夠成功,構造基類部分是經過友元實現的,和繼承方式無關。這裏的類B就是咱們定義的不可被繼承的類。

    最後,咱們嘗試讓類C繼承類B,那麼當類C實例化對象時(或顯式定義了構造函數),就會首先去構造屬於類B的部分。這裏注意,若是類B不是虛繼承類A的話,那麼這裏構造屬於類B的部分時,是經過類B構造類A部分,這必然是成功的。但咱們不但願,所以,這裏類B虛繼承了類A,當構造屬於類B部分時,因爲B虛繼承了A,那麼會由類C直接去訪問A,嘗試構造類A的部分,很明顯,因爲訪問不到類A的構造函數,所以C實例化對象失敗。以後,全部嘗試繼承類B的類都會在編譯時失敗(前提要求C顯示給出了構造,或類C實例化了對象)。

    到這裏,不可被繼承了類B建立成功,這裏很巧妙的應用了友元類的概念,從而實現了僅有B能夠實例化屬於A的部分。


    若是以爲這種方式太過靈活,不容易理解,那麼還有一種更加簡單的方式。C++11引入了final關鍵字,被該關鍵字修飾的類,都是不可被繼承的,這個和java中的用法基本是一致的。

class A final
{
public:
	A(){}
};

class B :public A        // 編譯出錯
{};


二、定義一個只能夠在棧上建立對象的類

    若是上面那種狀況理解的話,這裏應該不會太難。爲何只能夠在棧上建立對象?如何實現?其實很簡單,只要咱們只對外暴露出能夠在堆上建立對象的接口就能夠。代碼以下:

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;
}


    和以前同樣,將構造函數和析構函數都設置爲私有(由於這裏不涉及繼承,private和protected在這裏沒有區別),因爲構造函數都是私有的,沒法建立對象,所以,提供了靜態方法,該方法中經過new和delete實現了在堆上對象的獲取和釋放。


三、定義一個只能夠在棧上建立對象的類

    若是理解了上一種,就應該知道這裏該如何去作。只要咱們只提供在棧上獲取對象的方式便可,因爲棧空間是由操做系統維護了,沒有特殊須要,析構函數就沒有必要顯式給出,代碼以下:

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;
}


     只能在棧上或者只能在堆上建立的對象,實現起來原理是同樣的,類的構造和析構函數都設置爲私有,當個人接口函數提供的方法是從堆中建立的對象時,類就只能在堆上建立對象,當個人接口函數提供的是從棧上直接獲得的對象的話,類就只能夠在棧上建立對象。須要注意一點的是,建立棧上對象的時候,不能夠返回臨時對象的引用,這個就再也不多解釋。



------muhuizz整理

相關文章
相關標籤/搜索