c 中堆物件與棧物件

2021-06-04 15:43:17 字數 2745 閱讀 3243

使用棧物件的意外收穫

棧物件是在適當的時候建立,然後在適當的時候自動釋放的,也就是棧物件有自動管理功能。那麼棧物件會在什麼會自動釋放了?第一,在其生命期結束的時候;第二,在其所在的函式發生異常的時候。你也許說,這些都很正常啊,沒什麼大不了的。是的,沒什麼大不了的。但是只要我們再深入一點點,也許就有意外的收穫了。

棧物件,自動釋放時,會呼叫它自己的析構函式。如果我們在棧物件中封裝資源,而且在棧物件的析構函式中執行釋放資源的動作,那麼就會使資源洩漏的概率大大降低,因為棧物件可以自動的釋放資源,即使在所在函式發生異常的時候。實際的過程是這樣的:函式丟擲異常時,會發生所謂的stack_unwinding(堆疊回滾),即堆疊會展開,由於是棧物件,自然存在於棧中,所以在堆疊回滾的過程中,棧物件的析構函式會被執行,從而釋放其所封裝的資源。除非,除非在析構函式執行的過程中再次丟擲異常――而這種可能性是很小的,所以用棧物件封裝資源是比較安全的。基於此認識,我們就可以建立乙個自己的控制代碼或**來封裝資源了。智慧型指標(auto_ptr)中就使用了這種技術。在有這種需要的時候,我們就希望我們的資源封裝類只能在棧中建立,也就是要限制在堆中建立該資源封裝類的例項。

禁止產生堆物件

那麼怎樣禁止產生堆物件了?我們已經知道,產生堆物件的唯一方法是使用new操作,如果我們禁止使用new不就行了麼。再進一步,new操作執行時會呼叫operator new,而operator new是可以過載的。方法有了,就是使new operator 為private,為了對稱,最好將operator delete也過載為private。現在,你也許又有疑問了,難道建立棧物件不需要呼叫new嗎?是的,不需要,因為建立棧物件不需要搜尋記憶體,而是直接調整堆疊指標,將物件壓棧,而operator new的主要任務是搜尋合適的堆記憶體,為堆物件分配空間,這在上面已經提到過了。好,讓我們看看下面的示例**:

#include //需要用到c式記憶體分配函式

class resource ; //代表需要被封裝的資源類

class nohashobject

public: 

nohashobject() 

~nohashobject() 

resource* ptr ;//指向被封裝的資源

... ... //其它資料成員

private:

void* operator new(size_t size) //非嚴格實現,僅作示意之用

void operator delete(void* pp) //非嚴格實現,僅作示意之用

}; 難道在類nohashobject的定義不能改變的情況下,就一定不能產生該型別的堆物件了嗎?不,還是有辦法的,我稱之為「暴力破解法」:

void main(void)

禁止產生棧物件

前面已經提到了,建立棧物件時會移動棧頂指標以「挪出」適當大小的空間,然後在這個空間上直接呼叫對應的建構函式以形成乙個棧物件,而當函式返回時,會呼叫其析構函式釋放這個物件,然後再調整棧頂指標收回那塊棧記憶體。在這個過程中是不需要operator new/delete操作的,所以將operator new/delete設定為private不能達到目的。當然從上面的敘述中,你也許已經想到了:將建構函式或析構函式設為私有的,這樣系統就不能呼叫構造/析構函式了,當然就不能在棧中生成物件了。

這樣的確可以,而且我也打算採用這種方案。但是在此之前,有一點需要考慮清楚,那就是,如果我們將建構函式設定為私有,那麼我們也就不能用new來直接產生堆物件了,因為new在為物件分配空間後也會呼叫它的建構函式啊。所以,我打算只將析構函式設定為private。再進一步,將析構函式設為private除了會限制棧物件生成外,還有其它影響嗎?是的,這還會限制繼承。

如果乙個類不打算作為基類,通常採用的方案就是將其析構函式宣告為private。

為了限制棧物件,卻不限制繼承,我們可以將析構函式宣告為protected,這樣就兩全其美了。如下**所示:

class nostackobject

public:

void destroy() };

接著,可以像這樣使用nostackobject類:

nostackobject* hash_ptr = new nostackobject() ;

... ... //對hash_ptr指向的物件進行操作

hash_ptr->destroy() ; 

我們用new建立乙個物件,卻不是用delete去刪除它,而是要用destroy方法。很顯然,使用者是不習慣這種怪異的使用方式的。所以,我決定將建構函式也設為private或protected。這又回到了上面曾試圖避免的問題,即不用new,那麼該用什麼方式來生成乙個物件了?我們可以用間接的辦法完成,即讓這個類提供乙個static成員函式專門用於產生該型別的堆物件。(設計模式中的singleton模式就可以用這種方式實現。)讓我們來看看:

class nostackobject

~nostackobject()

public:

static nostackobject* creatinstance()

void destroy() };

現在可以這樣使用nostackobject類了:

nostackobject* hash_ptr = nostackobject::creatinstance() ;

... ... //對hash_ptr指向的物件進行操作

hash_ptr->destroy() ;

hash_ptr = null ; //防止使用懸掛指標

現在感覺是不是好多了,生成物件和釋放物件的操作一致了。

C 棧物件 堆物件 理解

在c 中,類的物件建立分為兩種,一種是靜態建立,如a a 另一種是動態建立,如a ptr new a 這兩種方式是有區別的。1 靜態建立類物件 是由編譯器為物件在棧空間中分配記憶體,是通過直接移動棧頂指標,挪出適當的空間,然後在這片記憶體空間上呼叫建構函式形成乙個棧物件。使用這種方法,直接呼叫類的建...

棧物件和堆物件

在gameres上看見乙個問題帖 什麼時候該用 object object 什麼時候該用 object object object new object 感覺看起來沒什麼區別,其實不一樣 前乙個是棧物件,後乙個是堆物件。引用一下別人對棧物件 堆物件的解釋 棧物件的優勢是在適當的時候自動生成,又在適當...

C 棧物件,堆物件,靜態物件的理解

的優勢是在適當的時候自動生成,又在適當的時候自動銷毀,不需要程式設計師操心 而且棧物件的建立速度一般較堆物件快,因為分配堆物件時,會呼叫operator new操作,operator new會採用某種記憶體空間搜尋演算法,而該搜尋過程可能是很費時間的,產生棧物件則沒有這麼麻煩,它僅僅需要移動棧頂指標...