首先需要指出的是,我們通常用“堆”和“自由存儲”這兩個術語來區分兩種不同類型的動態分配內存。
1.常量數據:常量數據區域主要用於存儲字符串以及其他在編譯期就已經知道值的數據。實例化的對象是不能存儲在 這 個區域中的。
在程序的整個生存期內,這個區域中的所有數據都是有效的。而且,所有這些數據都是只讀的,如果對這些數據進行修改,其結果在C++中是沒有定義的。造成這種後果的部分原因是編譯器可能會對常量數據的基本存儲格式進行任意優化。例如,在某個特定的編譯器中,可能會將字符串常量保存在重疊對象內以進行優化。
2.棧:在棧中存儲的是自動變量。自動變量在定義的時候被立即構造,並且在自動變量作用域結束的時候被立即銷毀,因此程序員無法對已經分配但尚未初始化的棧空間直接進行操作(除非你有意識地使用顯示析構函數和布局new語法)。
棧內存的分配通常要比動態內存的分配(堆和自由存儲)快很多,因為每次棧內存的分配只涉及棧指針的自增操作,而無需進行更為復雜的內存管理。
3.自由存儲:自由存儲時兩種動態內存區域之一,它是通過new/delete來分別進行分配/釋放。
對象的生存期可能會小於所分配的存儲空間的生存期。也就是說,自由存儲區域中的對象在分配內存時並不要求立即進行初始化,而且在銷毀對象時,也不要求立即釋放內存空間。在存儲空間已經被分配但還沒有進入到對象生存期的這段時間內,我們可以通過一個void*類型的指針來訪問和操作這塊存儲空間,但我們不能訪問對象中任何一個非靜態的成員或非靜態的成員函數,不能去獲得他們的地址,或者進行其他的操作。
4.堆:堆是另一種動態內存區域,它是通過malloc()/free()函數以及這些函數的其他形式來進行分配/釋放的。
我們要注意的是,盡管在某個特定的編譯器中,默認的全局運算符new和delete可能會用函數malloc()和free()來進行實現,但是堆還是不同於自由存儲,在堆中分配的內存不能再自由存儲區域中被安全地釋放,反之亦然 在堆中分配的內存,可以用於對象的placement new構造過程 和顯示的析構過程中。如果是這種用法,那麼自由存儲區域中關於對象生存期的注意事項也同樣適用於堆。
5.全局/靜態:在程序啟動的時候,這些變量/對象或靜態的變量/對象就已經被分配了存儲空間,但只有等到程序執行的時候,這些變量/對象才可以進行初始化。例如:函數中的靜態變量只有當程序第一次執行到變量的定義語句時才能被初始化。
對於跨越多個編譯單元的全局變量,它們的初始化順序是未定義的,並且我們在管理全局對象(包括類的靜態成員)之間的依賴性的時候要特別小心。通常來說,我們可以通過一個void*指針來對未初始化的對象存儲空間進行訪問和操作,但我們不能再對象的生存期之外來使用或者引用非靜態的成員變量或成員函數。
指導原則:我們應該優先使用自有存儲(new/delete),並且要避免去使用堆(malloc/free)。