摘要:“new”是C++的一個關鍵字,同時也是操作符。關于new的話題非常多,因為它确實比較複雜,也非常神秘。
本文分享自華為雲社群《如何編寫高效、優雅、可信代碼系列(2)——你真的會用new嗎》,原文作者:我是一顆大西瓜 。
C++記憶體管理
1. C++記憶體配置設定
C++中的程式加載到記憶體後按照代碼區、資料區、堆區、棧區進行布局,其中資料區又可以分為自由存儲區、全局/靜态存儲區和常量存儲區,各區所長如下:
-
棧區
函數執行的時候,局部變量的存儲單元都在棧上建立,函數執行結束後存儲單元會自動釋放。棧記憶體配置設定運算内置于處理器指令集中,效率高,但配置設定記憶體容量有限。
-
堆區
堆就是new出來的記憶體塊,編譯器不管釋放,由應用程式控制,new對應delete。如果沒釋放掉,程式結束後,作業系統會自動回收。
-
自由存儲區
C中malloc配置設定的記憶體塊。用free結束生命周期。
-
全局/靜态存儲區
全局變量和靜态變量被配置設定到同一塊記憶體中,定義的時候就會初始化。
-
常量存儲區
比較特殊的存儲區,存放常量,不允許修改。
堆和棧的差別
-
管理方式
棧由編譯器自動管理,堆由程式員控制
-
空間大小
32位系統下,堆記憶體可以達到4GB,棧有一定的空間大小
-
碎片管理
對于堆,頻繁的new/delete肯定造成記憶體空間的不連續,産生大量記憶體碎片降低程式效率;棧由于遵循先進後出的規則,不會産生空隙
-
生長方向
堆是向上生長的,即向着記憶體位址增加的方向增長;而棧是向着記憶體位址減小的方向增長的
-
配置設定方式
堆是動态配置設定的,棧有動态配置設定和靜态配置設定之分:靜态配置設定由編譯器完成,動态配置設定由alloca函數完成,即使是動态配置設定,依然是編譯器自動釋放
-
配置設定效率
計算機底層提供了棧的支援,配置設定了專門的寄存器存放棧的位址,壓棧出棧都有專門的指令執行,這決定了棧的效率會比較高。堆則是由C/C++函數庫提供的,機制比較複雜,比如為了配置設定某個大小的記憶體需要在堆記憶體中搜尋可用足夠大小的空間,效率比棧要低的多
2. new/delete和new []/delete []
- 回收new配置設定的單個對象記憶體空間時用delete,回收用new[]配置設定的一組對象時用delete[]
- 對于内置類型(int/double/float/char/…),由于new[]申請記憶體時,編譯器還會悄悄在記憶體中儲存整數,表示指針數組的個數,是以delete/delete[]都可以正确釋放所申請的記憶體空間
- 建議在調用new時使用的[],那麼調用delete也使用[]
3. new的三種形态
- new operator 常用的new,語言函數内建,不能重載。調用過程中實際完成的有三件事:
- 為類型對象配置設定記憶體;
- 調用構造函數初始化記憶體對象;
-
傳回對象指針
如果是在堆上建立對象,直接使用new operator。
- operator new 普通操作符,可以重載。如果僅僅是配置設定記憶體,那麼應該調用operator new,但不負責初始化。系統預設提供的配置設定器在時間和空間兩方面都存在一些問題:配置設定器速度較慢,配置設定小型對象時空間浪費嚴重,重載new/delete有三方面好處:
- 改善效率
- 檢測代碼中的記憶體錯誤
- 獲得記憶體使用的統計資料
- C++标準規定,重載的operator new必須是類成員函數或全局函數,全局的operator new重載不應該改變原有簽名,而是直接無縫替換原有版本。全局重載很有侵略性,别人使用你的庫無法使用預設的new,而具體類的重載隻會影響本class和其派生類,但是類的operator new函數重載必須聲明為static,因為operator new是在類的具體對象被建構出來之前調用的。
- 為了獲得2和3的優勢,重載的operator new需要如下函數聲明void* operator new(size_t, const char* file, int line);
- placement new 定義在庫<<new>>中。如果想在一塊已經獲得記憶體裡建立對象,那麼應該調用placement new。通常情況不建議使用,但在某些對時間要求非常高的應用中可以考慮,因為選擇合适的構造函數完成對象初始化是一個時間相對較長的過程。
點選關注,第一時間了解華為雲新鮮技術~