1. 程式人生 > >如何建立一個類只能在堆(棧)上建立物件

如何建立一個類只能在堆(棧)上建立物件

在C++中,類物件的建立方式有兩種,一種是靜態建立類物件,如A a;另一種是動態建立類物件,如A* p = new A;

這兩種方式是有區別的:

    靜態建立類物件:是指全域性物件,靜態物件,以及分配在棧區域內的物件,編譯器對它們的記憶體分配是在編譯階段就完成的,是通過直接移動棧頂指標,挪出適當的空間,然後在這片記憶體空間上呼叫建構函式形成一個棧物件。使用這種方法,直接呼叫類的建構函式。

    動態建立類物件:分配堆區域內的物件,編譯器對他們的記憶體分配是在執行時動態分配的,(使用new運算子將物件建立在堆空間中。這個過程分為兩步,第一步,執行operator new()函式,在對中搜索合適的記憶體並進行分配,第二步,呼叫建構函式構造物件,初始化這片記憶體空間。使用這種方法,間接呼叫類的建構函式。

知道了這些,那麼,如何限制類只能在堆上或棧上建立物件呢?

1.只能在堆上建立物件  

    類物件只能建立在堆上,就是不能靜態建立物件,即不能直接呼叫類的建構函式。

    很容易就想到把類的建構函式宣告為私有,就無法在類的外部呼叫類的建構函式建立物件,只能使用new運算子來建立物件。前面已經說過,new運算子的執行過程分為兩步,C++提供new運算子的過載其實只是允許過載operator new()函式,不能過載new運算子,而operator new()函式只用於分配記憶體,無法提供建構函式,所以,我們再定義一個GetObj函式,用於在堆上new物件,通過GetObj函式,建立的物件都是在堆上的new出來的,將函式宣告為靜態的,就可以通過域作用訪問符訪問GetObj函式,在堆上建立物件。(在C++中靜態成員函式也是類函式,及這個函式不屬於某個具體的物件,而是屬於一個類的,這個類例項化的每個成員都可用,同時,這個類也可以直接呼叫這個函式而不用例項化一個物件。)

  1. <span style="font-family:SimSun;font-size:18px;">class BB  
  2. {  
  3. public:  
  4.     static BB& GetObj(int b1 = 0,int b2 = 0)  
  5.     {  
  6.         return *(new BB(b1,b2));  
  7.     }  
  8. private:  
  9.     BB(int b1 = 0,int b2 = 0)  
  10.         :_b1(b1)  
  11.         ,_b2(b2)  
  12.     {}  
  13.     int _b1;  
  14.     int _b2;  
  15. };  
  16. void Test()  
  17. {  
  18.     BB b = BB::GetObj();  
  19. }</span>  
網上還有一種說法,就是將解構函式宣告為私有的,當物件建立在棧上面的時候,是由編譯器分配記憶體空間

的,呼叫建構函式來構造棧物件,當物件使用使用完之後編譯器會呼叫解構函式來釋放棧物件,編譯器管理了物件的

整個生命週期,如果編譯器無法呼叫解構函式來釋放空間,那會發生什麼情況呢?比如,類的解構函式是私有的,編

譯器無法呼叫解構函式來釋放記憶體。所以,編譯器在為類物件分配棧空間的時候,會先檢查類的解構函式的訪問性,

不只是析構函式,只要是非靜態的函式,編譯器都會檢查,如果類的解構函式是私有的,那麼編譯器就不會在棧上位

類物件分配記憶體。

那麼,到底為什麼將解構函式宣告為私有的就可以只在堆上建立物件呢?

因為c++是一個靜態繫結的語言,在編譯過程中,所有的非虛擬函式都必須分析完成,即使是虛擬函式,也需檢查可訪問

性,因此,當在棧上生成物件是,物件會自動析構,也就是說解構函式必須可訪問,而對上生成物件,由於析構是由

程式設計師控制,所以,就可以不用解構函式,而重新定義一個函式完成解構函式的功能,然後手動呼叫。

  1. class AA  
  2. {  
  3. public:  
  4.     AA()  
  5.     {}  
  6. protected:  
  7.     ~AA()  
  8.     {}  
  9. };  
  10. void Test()  
  11. {  
  12.     AA* a = new AA;  
  13. }  

那麼怎麼釋放它呢?  解構函式私有化的類的設計可以保證只能用new命令在堆(heap)中建立物件, 只能動態的去建立物件, 這樣可以自由的控制物件的生命週期. 但是, 這樣的類需要提供建立和撤銷的公共介面. 

  1. AA::void Destory()  
  2. {  
  3.     deletethis;  
  4. }  

  上述方法的一個缺點就是,無法解決繼承問題。如果A作為其它類的基類,則解構函式通常要設為virtual,然後在子類重寫,以實現多型。因此解構函式不能設為private。還好C++提供了第三種訪問控制,protected。將解構函式設為protected可以有效解決這個問題,類外無法訪問protected成員,子類則可以訪問。

這就證明了,如果要建立一個只能在堆上建立物件的類,無論將它的建構函式宣告成私有還是解構函式宣告為保護都可以達到這樣的目的,但是,如果將該類的建構函式或解構函式宣告為保護,最好將另外一個也宣告為保護,因為例如你用new建立一個物件, 卻不是用delete去刪除它, 而是要用destroy方法,很顯然,這是一種很奇怪的使用方法,所以,如果將該類的建構函式或解構函式宣告為保護,最好將另外一個也宣告為保護。

2.只能在棧上建立物件的類

只有使用new運算子,物件才會建立在堆上,所以只要禁用new運算子就可以實現類物件只能建立在棧上,將operator new()設為私有就可以了,一定要記得,過載了new就要過載delete

  1. class CC  
  2. {  
  3. public:  
  4.     CC()  
  5.     {}  
  6.     ~CC()  
  7.     {}  
  8. private:  
  9.     void* operator new(size_t)//過載operator new() ,注意函式返回值和引數都是固定的
  10.     {}  
  11.     void operator delete(void* ptr)//也要過載operator delete()
  12.     {}  
  13. };  

轉自http://blog.csdn.net/z_xiao_xue/article/details/53081804

參考:http://blog.csdn.net/g5dsk/article/details/4775144