C++:在堆上創建對象,還是在棧上?


這篇文章來自於一次討論:http://www.devbean.net/2013/01/qt-study-road-2-model-view/#comment-17532。關於究竟是在堆上還是在棧上創建對象,可能很多初學者感到迷惑。我想可以把這部分內容拿出來詳細介紹一下。現在,假設你已經清楚什么是堆,什么是棧。

如果需要在堆上創建對象,要么使用new運算符,要么使用malloc系列函數。這點沒有異議。

真正有異議的是下面的代碼:

Object obj; 

要回答這個問題,我們首先要理解這個語句是什么意思。這個語句就是代表着,在棧上創建對象嗎?

其實,這行語句的含義是,使對象obj具有“自動存儲(automatic storage)”的性質。所謂“自動存儲”,意思是這個對象的存儲位置取決於其聲明所在的上下文。

如果這個語句出現在函數內部,那么它就在棧上創建對象。

如果這個語句不是在函數內部,而是作為一個類的成員變量,則取決於這個類的對象是如何分配的。考慮下面的代碼:

class Class
{
    Object obj;
};
 
Class *pClass = new Class;

指針pClass所指向的對象在堆上分配空間。因為Object obj;語句的含義是“自動存儲”,所以,pClass->obj也是在堆上創建的。

理解了這一點,再來看下面的語句:

Object *pObj;
pObj = new Object;

Object *pObj;代表,指針pObj是自動存儲的,僅此而已,沒有任何其它含義。而下面一行語句則指出,這個指針所指向的對象是在堆上面分配的。如果這兩行語句出現在一個函數內部,意味着當函數結束時,pObj會被銷毀,但是它指向的對象不會。因此,為了繼續使用這個對象,通常我們會在函數最后添加一個return語句,或者使用一個傳出參數。否則的話,這個在堆上創建的對象就沒有指針指向它,也就是說,這個對象造成了內存泄露。

並不是說指針指向的對象都是在堆上創建的。下面的代碼則使用指針指向一個在棧上創建的對象:

至此,我們解釋了函數內部的變量和成員變量。還有兩類變量:全局變量和static變量。它們即不在堆上創建,也不在棧上創建。它們有自己的內存空間,是除堆和棧以外的數據區。也就是說,當Object obj即不在函數內部,又不是類的成員變量時,這個對象會在全局數據段創建,同理適用於static變量。對於指針Object *pObj;,如果這個語句出現在函數內部或類的成員變量,正如我們前面所說的,這個指針是自動存儲的。但是,如果這個語句是在類的外部,它就是在全局數據段創建的。雖然它指向的對象可能在堆上創建,也可能在棧上創建。

堆和棧的區別在於兩點:

  1. 生命周期
  2. 性能

第一點才是我們需要着重考慮的。由於棧的特性,如果你需要一個具有比其所在的上下文更長的生命周期的變量,只能在堆上創建它。所以,我們的推薦是:只要能在棧上創建對象,就在棧上創建;否則的話,如果你不得不需要更長的生命周期,只能選擇堆上創建。這是由於在棧上的對象不需要我們手動管理內存。有經驗的開發人員都會對內存管理感到頭疼,我們就是要避免這種情況的發生。總的來說,我們更多推薦選擇在棧上創建對象。

但是,有些情況,即便你在棧上創建了對象,它還是會占用堆的空間。考慮如下代碼:

void func
{
    std::vector v;
} 

對象v是在棧上創建的。但是,STL 的vector類其實是在堆上面存儲數據的(這點可以查看源代碼)。因此,只有對象v本身是在棧上的,它所管理的數據(這些數據大多數時候都會遠大於其本身的大小)還是保存在堆上。

關於第二點性能,有影響,不過一般可以忽略不計。確切的說,一般情況下你不需要考慮性能問題,除非它真的是一個問題。

首先,在堆上創建對象需要追蹤內存的可用區域。這個算法是由操作系統提供,通常不會是常量時間的。當內存出現大量碎片,或者幾乎用到 100% 內存時,這個過程會變得更久。與此相比,棧分配是常量時間的。其次,棧的大小是固定的,並且遠小於堆的大小。所以,如果你需要分配很大的對象,或者很多很多小對象,一般而言,堆是更好的選擇。如果你分配的對象大小超出棧的大小,通常會拋出一個異常。盡管很罕見,但是有時候也的確會發生。有關性能方面的問題,更多出現在嵌入式開發中:頻繁地分配、釋放內存可能造成碎片問題。

現代操作系統中,堆和棧都可以映射到虛擬內存中。在 32 位 Linux,我們可以把一個 2G 的數據放入堆中,而在 Mac OS 中,棧可能會限制為 65M。

總的來說,關於究竟在堆上,還是在棧上創建對象,首要考慮你所需要的生命周期。當性能真正成為瓶頸的時候,才去考慮性能的問題。堆和棧是提供給開發者的兩個不同的工具,不存在一個放之四海而皆准的規則告訴你,一個對象必須放在堆中還是在棧中。選擇權在開發者手中,決定權在開發者的經驗中。

轉自:https://www.devbean.net/2014/02/cpp-create-object-on-heap-or-stack/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM