天天看點

如何讓類對象隻在棧(堆)上配置設定空間?

轉自:http://blog.csdn.net/hxz_qlh/article/details/13135433

一般情況下,編寫一個類,是可以在棧或者堆配置設定空間。但有些時候,你想編寫一個隻能在棧或者隻能在堆上面配置設定空間的類。這能不能實作呢?仔細想想,其實也是可以滴。

在c++中,類的對象建立分為兩種,一種是靜态建立,如a a;另一種是動态建立,如a* ptr=new a;這兩種方式是有差別的。

1、靜态建立類對象:是由編譯器為對象在棧空間中配置設定記憶體,是通過直接移動棧頂指針,挪出适當的空間,然後在這片記憶體空間上調用構造函數形成一個棧對象。使用這種方法,直接調用類的構造函數。

2、動态建立類對象,是使用new運算符将對象建立在堆空間中。這個過程分為兩步,第一步是執行operator new()函數,在堆空間中搜尋合适的記憶體并進行配置設定;第二步是調用構造函數構造對象,初始化這片記憶體空間。這種方法,間接調用類的構造函數。

那麼如何限制類對象隻能在堆或者棧上建立呢?下面分别進行讨論。

1、隻能在堆上配置設定類對象,就是不能靜态建立類對象,即不能直接調用類的構造函數。

容易想到将構造函數設為私有。在構造函數私有之後,無法在類外部調用構造函數來構造類對象,隻能使用new運算符來建立對象。然而,前面已經說過,new運算符的執行過程分為兩步,c++提供new運算符的重載,其實是隻允許重載operator new()函數,而operatornew()函數隻用于配置設定記憶體,無法提供構造功能。是以,這種方法不可以。

當對象建立在棧上面時,是由編譯器配置設定記憶體空間的,調用構造函數來構造棧對象。當對象使用完後,編譯器會調用析構函數來釋放棧對象所占的空間。編譯器管理了對象的整個生命周期。如果編譯器無法調用類的析構函數,情況會是怎樣的呢?比如,類的析構函數是私有的,編譯器無法調用析構函數來釋放記憶體。是以,編譯器在為類對象配置設定棧空間時,會先檢查類的析構函數的通路性,其實不光是析構函數,隻要是非靜态的函數,編譯器都會進行檢查。如果類的析構函數是私有的,則編譯器不會在棧空間上為類對象配置設定記憶體。是以,将析構函數設為私有,類對象就無法建立在棧上了。代碼如下:

[cpp] view

plaincopy

class a  

{  

public:  

    a(){}  

    void destory(){delete this;}  

private:  

    ~a(){}  

};  

試着使用a a;來建立對象,編譯報錯,提示析構函數無法通路。這樣就隻能使用new操作符來建立對象,構造函數是公有的,可以直接調用。類中必須提供一個destory函數,來進行記憶體空間的釋放。類對象使用完成後,必須調用destory函數。

上述方法的缺點:

 一、無法解決繼承問題。如果a作為其它類的基類,則析構函數通常要設為virtual,然後在子類重寫,以實作多态。是以析構函數不能設為private。還好c++提供了第三種通路控制,protected。将析構函數設為protected可以有效解決這個問題,類外無法通路protected成員,子類則可以通路。

 二、類的使用很不友善,使用new建立對象,卻使用destory函數釋放對象,而不是使用delete。(使用delete會報錯,因為delete對象的指針,會調用對象的析構函數,而析構函數類外不可通路)這種使用方式比較怪異。為了統一,可以将構造函數設為protected,然後提供一個public的static函數來完成構造,這樣不使用new,而是使用一個函數來構造,使用一個函數來析構。代碼如下,類似于單例模式:

protected:  

    static a* create()  

    {  

        return new a();  

    }  

    void destory()  

        delete this;  

這樣,調用create()函數在堆上建立類a對象,調用destory()函數釋放記憶體。

2、隻能在棧上配置設定類對象

隻有使用new運算符,對象才會建立在堆上,是以,隻要禁用new運算符就可以實作類對象隻能建立在棧上。雖然你不能影響new operator的能力(因為那是c++語言内建的),但是你可以利用一個事實:new

operator 總是先調用 operator new,而後者我們是可以自行聲明重寫的。是以,将operator new()設為私有即可禁止對象被new在堆上。代碼如下:

    void* operator new(size_t t){}     // 注意函數的第一個參數和傳回值都是固定的  

    void operator delete(void* ptr){} // 重載了new就需要重載delete  

};  

繼續閱讀