1. 程式人生 > >c++ primer 筆記第十二章動態記憶體

c++ primer 筆記第十二章動態記憶體

第十二章:動態記憶體

梗概:本章主要講解使用C++智慧指標動態管理記憶體以及直接管理動態記憶體的方法以及他們的結合。

靜態記憶體儲存static變數以及定義在所有函式之外的變數。棧記憶體儲存函式之內的非static變數,堆記憶體用來分配給程式動態產生的物件。靜態記憶體和棧記憶體中的變數由編譯器決定其生命週期。靜態記憶體使用前分配,程式結束銷燬。棧記憶體的變數在該程式塊執行時存在。動態物件由程式控制宣告週期,即需要顯示分配和銷燬。

12.1 動態記憶體與智慧指標

new在動態記憶體中為物件分配空間並返回指向該物件的指標,delete接受一個動態物件的指標,銷燬物件並釋放記憶體。

忘記使用delete銷燬new分配的物件會造成記憶體洩漏。

新標準提供三種智慧指標,智慧指標可以自動釋放指向的物件。有shared_ptr, unique_ptr和weak_ptr三種。

 

12.1.1 shared_ptr 類

智慧指標是模板,建立需要提供型別。如shared_ptr<string> p1; 預設為空指標。

make_shared<T>(args)函式類似emplace,使用引數構造動態物件。引數為空則進行值初始化。

認為每個shared_ptr都有一個引用計數,有新共享指標指向共享指標指向的物件計數器會加一,銷燬減一。計數器為0物件銷燬。

shared_ptr呼叫類的解構函式銷燬物件並且釋放物件關聯的記憶體。

shared_ptr保證有共享指標指向的物件一直存在,因此需要保證刪除不需要的共享指標。特別是在容器中。

使用動態記憶體的三個原因:一、程式不清楚使用物件數量;二、不清楚物件型別;三、需要在多個物件之間共享資料。

 

12.1.2 直接管理記憶體

使用new和delete直接管理動態記憶體,包含此操作的類不能使用預設的解構函式和賦值拷貝。

new動態分配的物件是預設初始化的,類型別呼叫預設建構函式。也可以使用直接初始化、列表初始化和值初始化。

類型別的值初始化呼叫預設建構函式,內建型別值初始化為一個類似0的初值而非未定義。

動態分配const物件必須初始化。

為了防止記憶體耗盡丟擲異常,可以為new輸入引數nothrow,叫做定位new。

delete只能釋放使用new分配的物件,而且不能重複釋放。

內建指標動態分配的物件在顯示刪除之前一直存在。

為了避免重複釋放,delete指標之後把其置為空指標。但是無法保證另外的指標物件不被誤用。

 

12.1.3 shared_ptr 和 new 結合使用

可以使用new返回的指標初始化shared_ptr型別,如shared_ptr<int> p2(new int(42))。

shared_ptr不接受隱式轉換,只能使用直接初始化的形式不能使用拷貝初始化。返回shared_ptr型別的函式不能使用返回普通指標。

智慧指標預設使用delete銷燬物件,因此預設傳入可以被delete銷燬的物件,否則需要傳入自定的銷燬函式shared_ptr<T> p(q,d)。

shared_ptr能夠和同型別之間協調析構計數,但是和內建指標無法交流。因此用來構造智慧指標的內建指標物件無需delete。

也不能將同一塊內建指標型別構造不同的共享指標。構造了共享指標之後不應用內建指標繼續訪問。也不要使用共享指標的get構造另外的共享指標。

reset可以修改共享指標指向的記憶體。reset會更新引用計數,如果原物件沒有其它共享指標引用,會被銷燬。

 

12.1.4 智慧指標和異常

智慧指標能夠在程式丟擲異常時自動銷燬物件。而內建指標不能自動delete。

可以傳遞第二個引數作為智慧指標指向物件的銷燬函式,如shared_ptr<connection> p(&c, end_connection)。

 

12.1.5 unique_ptr

unique_ptr指向的物件只能被一個unique_ptr指向。必須採用直接初始化形式。

unique_ptr不能賦值和拷貝。可以reset轉移指向的物件,reset接受內建指標或空。

release放棄物件,返回指標,將自己置為空。通常用其返回值初始化其它智慧指標或者賦值。

可以拷貝或者賦值一個即將被銷燬的unique_ptr,如函式返回。

向unique_ptr傳遞刪除函式需要在尖括號中指定型別,如unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);

 

12.1.6 weak_ptr

weak_ptr指向一個共享指標指向的物件,不改變引用計數也不控制指標生存期。

使用shared_ptr初始化weak_ptr,可以直接初始化或賦值初始化。

不能用weak_ptr直接訪問物件,呼叫lock函式返回shared_ptr,若物件已銷燬返回空共享指標。

 

12.2 動態陣列

直接使用new分配多個物件,不如使用標準庫如vector等。

12.2.1 new 和陣列

new 型別名後方括號加數目,分配一個物件陣列。返回指向第一個物件的指標。

new一個動態陣列可以使用值初始化或者一個花括號列表初始化。若列表初始化數量多分配失敗,少的部分值初始化。

可以動態分配長度為0的陣列,但不能引用。

釋放動態陣列時使用delete[]。

可以使用unique_ptr智慧指標管理動態陣列,如unique_ptr<int[]> up(new int[10])。尖括號指定陣列型別。

up.release呼叫delete[]銷燬陣列。

當unique_ptr指向陣列時不能使用點運算子和箭頭運算子,可以使用下標運算子訪問元素。

shared_ptr預設不支援管理動態陣列,除非提供自定義的刪除器,一般用lambda函式。

 

12.2.2 allocator 類

allocator類將記憶體的分配和物件構造分離。

allocator<T> a; 初始化 a.allocate(n)分配記憶體; a.deallocate(p,n)收回記憶體; a.construct(p, args)在指標p處構造一個物件。

a.destroy(p)對p指向的物件執行解構函式,但沒有釋放記憶體。

deallocate必須釋放的是由allocator分配的記憶體,而且n的大小相等。

uninitialized_copy(b,e,b2) b2為指向未初始化的記憶體,b,e是迭代器。uninitialized_copy_n(b,n,b2)類似。返回遞增後的b2。

uninitialized_fill(b,e,t)將物件t拷貝到迭代器範圍(b,e)中。uninitialized_fill_n(b,n,t)類似。b指向未初始化的記憶體。

auto p = alloc.allocate(vi.size()*2); 

auto q = uninitialized_copy(vi.begin(), vi.end(), p);

uninitialized_fill_n(q, vi.size(), 42);