轉載於: http://blog.csdn.net/hxz_qlh/...
如遇侵權,麻煩聯繫刪除面試
昨天一個同窗去網易面試C++研發,問到了這麼一個問題:如何限制一個類對象只在棧(堆)上分配空間?函數
通常狀況下,編寫一個類,是能夠在棧或者堆分配空間。但有些時候,你想編寫一個只能在棧或者只能在堆上面分配空間的類。這能不能實現呢?仔細想一想,其實也是能夠滴。this
在C++中,類的對象創建分爲兩種,一種是靜態創建,如A a;另外一種是動態創建,如A* ptr=new A;這兩種方式是有區別的。.net
靜態創建類對象:
是由編譯器爲對象在棧空間中分配內存,是經過直接移動棧頂指針,挪出適當的空間,而後在這片內存空間上調用構造函數造成一個棧對象。使用這種方法,直接調用類的構造函數。指針
動態創建類對象:
是使用new運算符將對象創建在堆空間中。這個過程分爲兩步,第一步是執行operator new()函數,在堆空間中搜索合適的內存並進行分配;第二步是調用構造函數構造對象,初始化這片內存空間。這種方法,間接調用類的構造函數。code
那麼如何限制類對象只能在堆或者棧上創建呢?下面分別進行討論。對象
就是不能靜態創建類對象,即不能直接調用類的構造函數。blog
容易想到將構造函數設爲私有。在構造函數私有以後,沒法在類外部調用構造函數來構造類對象,只能使用new運算符
來創建對象。然而,前面已經說過,new運算符
的執行過程分爲兩步,C++
提供new運算符
的重載,實際上是隻容許重載operator new()
函數,而operator new()
函數只用於分配內存,沒法提供構造功能。所以,這種方法不能夠。繼承
當對象創建在棧上面時,是由編譯器分配內存空間的,調用構造函數來構造棧對象。當對象使用完後,編譯器會調用析構函數來釋放棧對象所佔的空間。編譯器管理了對象的整個生命週期。若是編譯器沒法調用類的析構函數,狀況會是怎樣的呢?好比,類的析構函數是私有的,編譯器沒法調用析構函數來釋放內存。因此,編譯器在爲類對象分配棧空間時,會先檢查類的析構函數的訪問性,其實不光是析構函數,只要是非靜態的函數,編譯器都會進行檢查。若是類的析構函數是私有的,則編譯器不會在棧空間上爲類對象分配內存。生命週期
所以,將析構函數設爲私有,類對象就沒法創建在棧上了。
代碼以下:
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
,而是使用一個函數來構造,使用一個函數來析構。
代碼以下,相似於單例模式:
class A { protected: A(){} ~A(){} public: static A* create() { return new A(); } void destory() { delete this; } };
這樣,調用create()函數在堆上建立類A對象,調用destory()函數釋放內存。
只有使用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(){} };