如何讓類物件只在棧或堆上分配空間?
在C++中,類的物件建立分為兩種,一種是靜態建立,如A a;另一種是動態建立,如A* ptr=new A;
1、靜態建立類物件:是由編譯器為物件在棧空間中分配記憶體,是通過直接移動棧頂指標,挪出適當的空間,然後在這片記憶體空間上呼叫建構函式形成一個棧物件。使用這種方法,直接呼叫類的建構函式。
2、動態建立類物件:是使用new運算子將物件建立在堆空間中。這個過程分為兩步,第一步是執行operator new()函式,在堆空間中搜索合適的記憶體並進行分配;第二步是呼叫建構函式構造物件,初始化這片記憶體空間。這種方法,間接呼叫類的建構函式。
那麼如何限制類物件只能在堆或者棧上建立呢?下面分別進行討論。
1、只能在堆上分配類物件,就是不能靜態建立類物件,即不能直接呼叫類的建構函式。
容易想到將建構函式設為私有。在建構函式私有之後,無法在類外部呼叫建構函式來構造類物件,只能使用new運算子來建立物件。然而,前面已經說過,new運算子的執行過程分為兩步,C++提供new運算子的過載,其實是隻允許過載operator new()函式,而operatornew()函式只用於分配記憶體,無法提供構造功能。因此,這種方法不可以。
當物件建立在棧上面時,是由編譯器分配記憶體空間的,呼叫建構函式來構造棧物件。當物件使用完後,編譯器會呼叫解構函式來釋放棧物件所佔的空間。編譯器管理了物件的整個生命週期。如果編譯器無法呼叫類的解構函式,情況會是怎樣的呢?比如,類的解構函式是私有的,編譯器無法呼叫解構函式來釋放記憶體。所以,編
程式碼如下:
- class A
- {
- public:
- A(){}
- void destory(){deletethis;}
- private:
- ~A(){}
- };
上述方法的缺點:
一、無法解決繼承問題。
如果A作為其它類的基類,則解構函式通常要設為virtual,然後在子類重寫,以實現多型。因此解構函式不能設為private。還好C++提供了第三種訪問控制,protected。將解構函式設為protected可以有效解決這個問題,類外無法訪問protected成員,子類則可以訪問。
二、類的使用很不方便,使用new建立物件,卻使用destory函式釋放物件,而不是使用delete。
使用delete會報錯,因為delete物件的指標,會呼叫物件的解構函式,而解構函式類外不可訪問,這種使用方式比較怪異。為了統一,可以將建構函式設為protected,然後提供一個public的static函式來完成構造,這樣不使用new,而是使用一個函式來構造,使用一個函式來析構。
程式碼如下(類似於單例模式):
- class A
- {
- protected:
- A(){}
- ~A(){}
- public:
- static A* create()
- {
- returnnew A();
- }
- void destory()
- {
- deletethis;
- }
- };
這樣,呼叫create()函式在堆上建立類A物件,呼叫destory()函式釋放記憶體。
2、只能在棧上分配類物件
只有使用new運算子,物件才會建立在堆上,因此,只要禁用new運算子就可以實現類物件只能建立在棧上。雖然你不能影響new operator的能力(因為那是C++語言內建的),但是你可以利用一個事實:new operator 總是先呼叫 operator new,而後者我們是可以自行宣告重寫的。因此,將operator new()設為私有即可禁止物件被new在堆上。
程式碼如下:
- class A
- {
- private:
- void* operator new(size_t t){} // 注意函式的第一個引數和返回值都是固定的
- void operator delete(void* ptr){} // 過載了new就需要過載delete
- public:
- A(){}
- ~A(){}
- };