C 物件記憶體分配問題

2021-07-01 19:01:51 字數 2895 閱讀 7260

c++將記憶體劃分為三個邏輯區域:堆、棧和靜態儲存區。既然如此,我稱位於它們之中的物件分別為堆物件,棧物件以及靜態物件。

先來看看棧。棧,一般用於存放區域性變數或物件,如我們在函式定義中用類似下面語句宣告的物件:

type stack_object ;

stack_object便是乙個棧物件,它的生命期是從定義點開始,當所在函式返回時,生命結束。

另外,幾乎所有的臨時物件都是棧物件。比如,下面的函式定義:

type fun(type object) ;

這個函式至少產生兩個臨時物件,首先,引數是按值傳遞的,所以會呼叫拷貝建構函式生成乙個臨時物件object_copy1 ,在函式內部使用的不是使用的不是object,而是object_copy1,自然,object_copy1是乙個棧物件,它在函式返回時被釋放;還有這個函式是值返回的,在函式返回時,如果我們不考慮返回值優化(nrv),那麼也會產生乙個臨時物件object_copy2,這個臨時物件會在函式返回後一段時間內被釋放。比如某個函式中有如下**:

type tt ,result ; //生成兩個棧物件

tt = fun(tt) ; //函式返回時,生成的是乙個臨時物件object_copy2

上面的第二個語句的執**況是這樣的,首先函式fun返回時生成乙個臨時物件object_copy2 ,然後再呼叫賦值運算子執行

tt = object_copy2 ; //呼叫賦值運算子

看到了嗎?編譯器在我們毫無知覺的情況下,為我們生成了這麼多臨時物件,而生成這些臨時物件的時間和空間的開銷可能是很大的,所以,你也許明白了,為什麼對於「大」物件最好用const引用傳遞代替按值進行函式引數傳遞了。

接下來,看看堆。堆,又叫自由儲存區,它是在程式執行的過程中動態分配的,所以它最大的特性就是動態性。在c++中,所有堆物件的建立和銷毀都要由程式設計師負責,所以,如果處理不好,就會發生記憶體問題。如果分配了堆物件,卻忘記了釋放,就會產生記憶體洩漏;而如果已釋放了物件,卻沒有將相應的指標置為null,該指標就是所謂的「懸掛指標」,再度使用此指標時,就會出現非法訪問,嚴重時就導致程式崩潰。

那麼,c++中是怎樣分配堆物件的?唯一的方法就是用new(當然,用類malloc指令也可獲得c式堆記憶體),只要使用new,就會在堆中分配一塊記憶體,並且返回指向該堆物件的指標。

再來看看靜態儲存區。所有的靜態物件、全域性物件都於靜態儲存區分配。關於全域性物件,是在main()函式執行前就分配好了的。其實,在main()函式中的顯示**執行之前,會呼叫乙個由編譯器生成的_main()函式,而_main()函式會進行所有全域性物件的的構造及初始化工作。而在main()函式結束之前,會呼叫由編譯器生成的exit函式,來釋放所有的全域性物件。比如下面的**:

void main(void)

實際上,被轉化成這樣:

void main(void)

所以,知道了這個之後,便可以由此引出一些技巧,如,假設我們要在main()函式執行之前做某些準備工作,那麼我們可以將這些準備工作寫到乙個自定義的全域性物件的建構函式中,這樣,在main()函式的顯式**執行之前,這個全域性物件的建構函式會被呼叫,執行預期的動作,這樣就達到了我們的目的。 剛才講的是靜態儲存區中的全域性物件,那麼,區域性靜態物件了?區域性靜態物件通常也是在函式中定義的,就像棧物件一樣,只不過,其前面多了個static關鍵字。區域性靜態物件的生命期是從其所在函式第一次被呼叫,更確切地說,是當第一次執行到該靜態物件的宣告**時,產生該靜態區域性物件,直到整個程式結束時,才銷毀該物件。

還有一種靜態物件,那就是它作為class的靜態成員。考慮這種情況時,就牽涉了一些較複雜的問題。

第乙個問題是class的靜態成員物件的生命期,class的靜態成員物件隨著第乙個class object的產生而產生,在整個程式結束時消亡。也就是有這樣的情況存在,在程式中我們定義了乙個class,該類中有乙個靜態物件作為成員,但是在程式執行過程中,如果我們沒有建立任何乙個該class object,那麼也就不會產生該class所包含的那個靜態物件。還有,如果建立了多個class object,那麼所有這些object都共享那個靜態物件成員。

第二個問題是,當出現下列情況時:

class base

class derived1 : public base / / 公共繼承

class derived2 : public base / / 公共繼承

base example ;

derivde1 example1 ;

derivde2 example2 ;

example.s_object = …… ;

example1.s_object = …… ;

example2.s_object = …… ;

請注意上面標為黑體的三條語句,它們所訪問的s_object是同乙個物件嗎?答案是肯定的,它們的確是指向同乙個物件,這聽起來不像是真的,是嗎?但這是事實,你可以自己寫段簡單的**驗證一下。我要做的是來解釋為什麼會這樣? 我們知道,當乙個模擬如derived1,從另乙個模擬如base繼承時,那麼,可以看作乙個derived1物件中含有乙個base型的物件,這就是乙個subobject。乙個derived1物件的大致記憶體布局如下:

讓我們想想,當我們將乙個derived1型的物件傳給乙個接受非引用base型引數的函式時會發生切割,那麼是怎麼切割的呢?相信現在你已經知道了,那就是僅僅取出了derived1型的物件中的subobject,而忽略了所有derived1自定義的其它資料成員,然後將這個subobject傳遞給函式(實際上,函式中使用的是這個subobject的拷貝)。

所有繼承base類的派生類的物件都含有乙個base型的subobject(這是能用base型指標指向乙個derived1物件的關鍵所在,自然也是多型的關鍵了),而所有的subobject和所有base型的物件都共用同乙個s_object物件,自然,從base類派生的整個繼承體系中的類的例項都會共用同乙個s_object物件了。上面提到的example、example1、example2的物件布局如下圖所示:

c 記憶體分配問題

首先分為 區和資料區 資料區分為 靜態資料區,動態資料,動態資料區 堆區和棧區 區存放程式 靜態資料區 存放編譯時就分配記憶體的變數,生命週期貫穿整個程式執行過程,所有程式執行完畢後自動釋放。堆區 由程式猿安排分配和釋放的變數,通過malloc free.new delete管理。棧區 存放函式的形...

C 記憶體分配問題

寫得比較好 c 記憶體分配問題 網上看到,感覺講得很清晰,剛好能解答我上次的筆試題 char r hello word char b hello word r w b w 其實應該是語法錯誤,可是vc 6.0沒有警告或者錯誤,r指向的是文字常量區,此區域是編譯的時候確定的,並且程式結束的時候自動釋放...

C 物件記憶體分配總結

目錄 六條基本規則 c 程式占用記憶體分類 例項 參考 如果上面常量區 區這些知識沒有概念,看一下下面的總結 堆區 heap 一般由程式設計師自動分配,如果程式設計師沒有釋放,程式結束時可能有os 其分配類似於鍊錶。全域性區 靜態區static 存放全域性變數 靜態資料 常量。程式結束後由系統釋放。...