C++編程語言是一門比較高深的計算機應用語言。它的很多功能都需要我們在不斷的學習與實踐過程中去探索。比如C++內存相關內容,就是其中一個重要的知識點。C++內存邏輯區域總共被分為三種:堆、棧和靜態存儲區。我們稱位於它們之中的對象分別為堆對象,棧對象以及靜態對象。
C++內存邏輯區域之a棧:一般用於存放局部變量或對象,如我們在函數定義中用類似下面語句聲明的對象:
Type stack_object ;
stack_object便是一個棧對象,生命周期是從定義點開始,函數返回時,結束。幾乎所有的臨時對象都是棧對象。比如,下面的函數定義:
Type funType object) ;
這個函數至少產生兩個臨時對象,首先,參數是按值傳遞的,所以會調用拷貝構造函數生成一個臨時對象object_copy1 ,在函數內部使用的不是使用的不是object,而是object_copy1,自然,object_copy1是一個棧對象,它在函數返回時被釋放;還有這個函數是值返回的,在函數返回時,如果我們不考慮返回值優化NRV),那麼也會產生一個臨時對象object_copy2,這個臨時對象會在函數返回後一段時間內被釋放。比如某個函數中有如下代碼:
Type tt ,result ; 生成兩個棧對象
tt = funtt) ; 函數返回時,生成的是一個臨時對象object_copy2
上面的第二個語句的執行情況是這樣的,首先函數fun返回時生成一個臨時對象object_copy2 ,然後再調用賦值運算符執行tt = object_copy2 ; 調用賦值運算符 編譯器在我們毫無知覺的情況下,為我們生成了這麼多臨時對象,而生成這些臨時對象的時間和空間的開銷可能是很大的,所以,對於“大”對象最好用const引用傳遞代替按值進行函數參數傳遞了。
C++內存邏輯區域之b 堆 又叫自由存儲區,它是在程序執行的過程中動態分配的,其最大的特性就是動態性。在C++中,所有堆對象的創建和銷毀都要由程序員負責,如果處理不好,就會發生內存問題。如分配了內存而沒有釋放則造成內存洩漏;如果已釋放了對象,卻沒有將相應的指針置為NULL,則可能會造成“懸掛指針”或“野指針”,再度使用此指針時,就會出現非法訪問,嚴重時就導致程序崩潰。
C++中一般通過new來為對象分配堆內存空間當然,用malloc也可獲得C式堆內存),並且返回指向該堆對象的指針。
C++內存邏輯區域之c 靜態存儲區 所有的靜態對象、全局對象都於靜態存儲區分配。關於全局對象,是在main()函數執行前就分配好了的。其實,在main()函數中的顯示代碼執行之前,會調用一個由編譯器生成的_main()函數,而_main()函數會進行所有全局對象的的構造及初始化工作。而在main()函數結束之前,會調用由編譯器生成的exit函數,來釋放所有的全局對象。比如下面的代碼:
- void mainvoid)
- {
- … … 顯式代碼
- }
實際上,被轉化成這樣:
- void mainvoid)
- {
- _main); 隱式代碼,由編譯器產生,用以構造所有全局對象
- … … 顯式代碼
- … …
- exit) ; 隱式代碼,由編譯器產生,用以釋放所有全局對象
- }
假設我們要在main()函數執行之前做某些准備工作,那麼我們可以將這些准備工作寫到一個自定義的全局對象的構造函數中,這樣,在main()函數的顯式代碼執行之前,這個全局對象的構造函數會被調用,執行預期的動作,這樣就達到了我們的目的。剛才講的是靜態存儲區中的全局對象,那麼,也有對應的局部靜態對象,局部靜態對象通常也是在函數中定義的,就像棧對象一樣,只不過,其前面多了個static關鍵字。局部靜態對象的生命期是從其所在函數第一次被調用,更確切地說,是當第一次執行到該靜態對象的聲明代碼時,產生該靜態局部對象,直到整個程序結束時,才銷毀該對象。還有一種靜態對象,那就是它作為class的靜態成員。考慮這種情況時,就牽涉了一些較復雜的問題。
第一個問題是class的靜態成員對象的生命期,class的靜態成員對象隨著第一個class object的產生而產生,在整個程序結束時消亡。也就是有這樣的情況存在,在程序中我們定義了一個class,該類中有一個靜態對象作為成員,但是在程序執行過程中,如果我們沒有創建任何一個該class object,那麼也就不會產生該class所包含的那個靜態對象。還有,如果創建了多個class object,那麼所有這些object都共享那個靜態對象成員。
第二個問題是,當出現下列情況時:
- class Base
- {
- public
- static Type s_object ;
- }
- class Derived1 public Base 公共繼承
- {
- … … other data
- }
- class Derived2 public Base 公共繼承
- {
- … … other data
- }
- Base example ;
- Derivde1 example1 ;
- Derivde2 example2 ;
- example.s_object = …… ;
- example1.s_object = …… ;
- example2.s_object = …… ;
請注意上面標為黑體的三條語句,它們所訪問的s_object是同一個對象嗎?答案是肯定的, 我們知道,當一個類比如Derived1,從另一個類比如Base繼承時,那麼,可以看作一個Derived1對象中含有一個Base型的對象,這就是一個subobject。當我們將一個Derived1型的對象傳給一個接受非引用Base型參數的函數時會發生切割,那麼是怎麼切割的呢?相信現在你已經知道了,那就是僅僅取出了Derived1型的對象中的subobject,而忽略了所有Derived1自定義的其它數據成員,然後將這個subobject傳遞給函數實際上,函數中使用的是這個subobject的拷貝)。
所有繼承Base類的派生類的對象都含有一個Base型的subobject這是能用Base型指針指向一個Derived1對象的關鍵所在,自然也是多態的關鍵了),而所有的subobject和所有Base型的對象都共用同一個s_object對象,從Base類派生的整個繼承體系中的類的實例都會共用同一個s_object對象了。
以上就是對C++內存邏輯區域的相關介紹。