C++中對於內存的相關知識是一個比較重要而且復雜的知識點,我們需要不斷的深入的對其進行研究。今天在這裡我們將會針對C++堆對象的一些應用方法做一個詳細的介紹,應該可以幫助大家對此有一個深刻的認識。
產生C++堆對象的方法是使用new操作,如果我們禁止使用new不就行了麼。再進一步,new操作執行時會調用operator new,而operator new是可以重載的。方法有了,就是使new operator 為private,為了對稱,最好將operator delete也重載為private。
現在,你也許又有疑問了,難道創建棧對象不需要調用new嗎?是的,不需要,因為創建棧對象不需要搜索內存,而是直接調整堆棧指針,將對象壓棧,而operator new的主要任務是搜索合適的堆內存,為C++堆對象分配空間,這在上面已經提到過了。好,讓我們看看下面的示例代碼:
- #include stdlib.h 需要用到C式內存分配函數
- class Resource ; 代表需要被封裝的資源類
- class NoHashObject
- {
- private
- Resource ptr ;指向被封裝的資源
- ... ... 其它數據成員
- void operator new(size_t size) 非嚴格實現,僅作示意之用
- {
- return malloc(size) ;
- }
- void operator delete(void pp) 非嚴格實現,僅作示意之用
- {
- free(pp) ;
- }
- public
- NoHashObject()
- {
- 此處可以獲得需要封裝的資源,並讓ptr指針指向該資源
- ptr = new Resource() ;
- }
- ~NoHashObject()
- {
- delete ptr ; 釋放封裝的資源
- }
- };
NoHashObject現在就是一個禁止C++堆對象的類了,如果你寫下如下代碼:
- NoHashObject fp = new NoHashObject() ; 編譯期錯誤!
- delete fp ;
上面代碼會產生編譯期錯誤。好了,現在你已經知道了如何設計一個禁止堆對象的類了,你也許和我一樣有這樣的疑問,難道在類NoHashObject的定義不能改變的情況下,就一定不能產生該類型的C++堆對象了嗎?不,還是有辦法的,我稱之為“暴力破解法”。C++是如此地強大,強大到你可以用它做你想做的任何事情。這裡主要用到的是技巧是指針類型的強制轉換。
- void main(void)
- {
- char temp = new char[sizeof(NoHashObject)] ;
強制類型轉換,現在ptr是一個指向NoHashObject對象的指針
- NoHashObject obj_ptr = (NoHashObject)temp ;
temp = NULL ; 防止通過temp指針修改NoHashObject對象
再一次強制類型轉換,讓rp指針指向堆中NoHashObject對象的ptr成員
- Resource rp = (Resource)obj_ptr ;
初始化obj_ptr指向的NoHashObject對象的ptr成員
- rp = new Resource() ;
現在可以通過使用obj_ptr指針使用堆中的NoHashObject對象成員了
- ... ...
- delete rp ;釋放資源
- temp = (char)obj_ptr ;
- obj_ptr = NULL ;防止懸掛指針產生
- delete [] temp ;釋放NoHashObject對象所占的堆空間。
- }
上面的實現是麻煩的,而且這種實現方式幾乎不會在實踐中使用,但是我還是寫出來路,因為理解它,對於我們理解C++內存對象是有好處的。對於上面的這麼多強制類型轉換,其最根本的是什麼了?我們可以這樣理解:
某塊內存中的數據是不變的,而類型就是我們戴上的眼鏡,當我們戴上一種眼鏡後,我們就會用對應的類型來解釋內存中的數據,這樣不同的解釋就得到了不同的信息。
所謂強制類型轉換實際上就是換上另一副眼鏡後再來看同樣的那塊內存數據。
另外要提醒的是,不同的編譯器對對象的成員數據的布局安排可能是不一樣的,比如,大多數編譯器將NoHashObject的ptr指針成員安排在對象空間的頭4個字節,這樣才會保證下面這條語句的轉換動作像我們預期的那樣執行:
- Resource rp = (Resource)obj_ptr ;
但是,並不一定所有的編譯器都是如此。既然我們可以禁止產生某種類型的C++堆對象,那麼可以設計一個類,使之不能產生棧對象嗎?當然可以。