動態記憶體與智慧型指標

2021-08-01 22:05:44 字數 4624 閱讀 2472

我們先來看一些物件的生存期。全域性物件在程式啟動時分配,在程式結束時銷毀。區域性static物件在第一次使用前分配,在程式結束時銷毀。區域性自動物件,在進入其定義所在的程式塊兒時被建立,離開塊時銷毀。即,它們都是由編譯器自動建立與銷毀。

而動態分配的物件的生存期與它們在**建立的無關,只有當顯式地被釋放時,這些物件才銷毀。

在c++中,動態記憶體的管理是通過一對運算子來完成的:

new:在動態記憶體為物件分配空間並返回乙個指向該物件的指標,可以選擇對物件進行初始化。

delete:接受乙個動態物件的指標,銷毀該物件,並釋放與之關聯的記憶體。

動態記憶體的使用很容易出問題,這裡列舉如下幾點:
緩衝區溢位

空懸指標/野指標

重複釋放: 釋放已經被釋放過了的記憶體

記憶體洩漏: 忘記釋放記憶體

不配對的new/delete

先列這幾條,等先了解智慧型指標的概念以後在來看怎麼用智慧型指標解決這些問題。

為了更安全(同時也更容易)地使用動態記憶體,標準庫提供了智慧型指標型別來管理動態物件。智慧型指標的行為類似於常規指標,重要的區別是它負責自動釋放所指向的物件。

我們首先來看一下shared_ptr類,該型別在memory標頭檔案中。

shared_ptr類

智慧型指標也是模板。因此在建立乙個智慧型指標時,必須提供它可以指向的型別。

....

shared_ptr

p1; //shared_ptr,可以指向string

shared_ptr

> p2; //shared_ptr,可以指向int的list

....

預設初始化的智慧型指標儲存著乙個空指標。類似與普通指標,解引用乙個智慧型指標返回它指向的物件。如果在乙個條件判斷中使用智慧型指標,效果是檢測它是否為空。

//如果p1指向乙個空string,解引用p1,將乙個新值給string

if(p1 && p1->empty())

最安全的分配和使用動態記憶體的方法是呼叫標準庫函式make_shared。定義在標頭檔案memory中。該函式在動態記憶體中分配乙個物件並初始化,返回指向此物件的shared_ptr。make_shared也是乙個模板,使用方法如下:

//指向乙個值為42的int物件的shared_ptr

shared_ptr

p3 = make_shared(42);

//p4指向乙個"9999999999"的string物件

shared_ptr

p4 = make_shared(10,'9');

//p5指向乙個值初始化的int,即值為0

shared_ptr

p5 = make_shared();

make_shared用其引數來構造給定型別的物件。例如,呼叫make_shared時傳遞的引數必須與string的某個建構函式相匹配。通常用auto定義乙個物件來儲存make_shared的結果:

//p6指向乙個動態分配的vector

auto p6 = make_shared>();

shared_ptr的拷貝和賦值

當進行拷貝或賦值操作時,每個shared_ptr都會記錄有多少個其他的shared_ptr指向相同的物件。

auto p = make_shared(42);   //p指向的物件只有p乙個引用者

auto q(p); //p和q指向相同的物件,此物件有兩個引用者。

可以認為每個shared_ptr都有乙個關聯的計數器(引用計數)。無論何時,拷貝乙個shared_ptr,計數器都會遞增。例如:

用乙個shared_ptr初始化另外乙個shared_ptr

將乙個shared_ptr作為乙個引數傳遞給乙個函式

當乙個shared_ptr作為乙個函式的返回值

相對的,給乙個shared_ptr賦予乙個新值或者shared_ptr被銷毀時,計數器就會遞減。

一旦乙個shared_ptr的計數器變為0,它就會自動釋放自己所管理的物件。

auto r = make_shared(42);

r = q; //q指向的物件的引用計數遞增。

//r原來指向的物件的引用計數遞減。

//r原來指向的物件沒有了引用者,會自動釋放。

當指向乙個物件的最後乙個shared_ptr被銷毀時,shared_ptr類會自動銷毀此物件,它是通過析構函式來完成銷毀動作的。shared_ptr的析構函式會遞減它所指向的物件的引用計數,如果引用計數邊為0,shared_ptr的析構函式就會銷毀物件,並釋放它占用的記憶體。

對於一塊記憶體,shared_ptr類保證只要有任何shared_ptr物件引用它,它就不會被釋放掉。

但是,如果你忘記了銷毀程式不需要的shared_ptr,程式仍會正確執行,但會浪費記憶體。一種可能的情況是,將shared_ptr放在乙個容器裡,當不需要某些元素時,應該確保用erase刪除那些不需要的shared_ptr元素。

程式使用動態生存期的資源的類 出於以下三種原因之一:

程式不知道自己需要使用多少物件(空間)例如,容器類

程式不知道所需物件的準確型別

程式需要在多個物件間共享資料

下面是乙個需要在多個物件間共享資料的例子:

我們定義乙個strblob類,儲存一組元素。與容器不同,我們希望strblob物件的不同拷貝之間共享相同的元素。我們可以用乙個vector來儲存元素,但是,不能在乙個strblob物件內直接儲存vector,因為乙個物件的資料成員在物件銷毀時也會被銷毀。為了實現strblob物件共享相同的底層資料,我們可以將vector儲存在動態記憶體中,然後為每個strblob設定乙個shared_ptr來管理動態記憶體分配的vector。這個shared_ptr的成員將記錄有多少個shared_ptr共享相同的vector,並在vector的最後乙個使用者被銷毀時釋放vector。

#ifndef _strblob_h

#define _strblob_h

#include

#include

#include

#include

using

namespace

std;

class strblob

}private:

std::shared_ptr

> data_;

};strblob::strblob():data_( make_shared>() ){}

strblob::strblob(initializer_list s):data_(make_shared>(s)){}

#endif

下面是乙個測試用例

#include"strblob.h"

int main(int argc,char *argv)

; strblob a; //使用不帶引數的建構函式

strblob b(s); //使用帶引數的建構函式

a.print();

b.print();

std::cout

<< std::endl;

//b物件的shred_ptr指向的物件的引用計數遞增,a的遞減並且釋放a的shared_ptr指向的物件

a = b; //使用預設賦值

a.print();

b.print();

return

0;}

其他shared_ptr操作:

我們可以用reset來將乙個新的指標賦予乙個shared_ptr

p.reset();     //若p是唯一指向物件的shared_ptr,reset會釋放此物件。

p.reset(q); //若傳遞了可選引數內建指標q,會令p指向q,否則會將p置為空。

p.reset(q,d); //如果還傳遞了引數d,將會呼叫d而不是delete來釋放q

weak_ptr

weak_ptr是一種不控制所指向物件生存期的智慧型指標,它指向由乙個shared_ptr管理的物件。將乙個weak_ptr繫結到shared_ptr不會改變shared_ptr的引用計數。一旦最後乙個指向物件的shared_ptr被銷毀,物件就會被釋放。即使有weak_ptr指向物件,物件也還是會被釋放,因此weak_ptr的名字抓住了這種智慧型指標「弱」共享物件的特點。

當我們建立乙個weak_ptr時,要用乙個shared_ptr來初始化它:

auto p = make_shared(42);

weak_ptr wp(p); //wp弱共享p; p的引用計數未改變。

由於waek_ptr指向的物件可能不存在,因此我們不能直接使用weak_ptr來訪問物件,而必須呼叫lock()。此函式檢查weak_ptr指向的物件是否仍存在,如果存在,lock返回乙個指向共享物件的shared_ptr。與任何其他shared_ptr類似,只要此shared_ptr存在,它所指向的底層物件也就會一直存在。

if(auto np = wp.lock())

動態記憶體與智慧型指標

動態記憶體與智慧型指標 靜態記憶體用來儲存區域性static物件 類static資料成員以及定義在任何函式之外的變數 全域性變數 棧記憶體用來儲存定義在函式內的非static物件。分配在靜態或棧記憶體中的物件由編譯器自動建立和銷毀。對於棧物件,僅在其定義的程式塊執行時才存在 static物件在使用之...

動態記憶體與智慧型指標

在c 中,動態記憶體的管理是通過一對運算子來完成的 new,在動態記憶體中為物件分配空間並返回乙個指向該物件的指標,我們可以選擇對物件進行初始化 delete,接受乙個動態物件指標,銷毀該物件並釋放與之關聯的記憶體。動態記憶體在使用時很容易出問題,有時會忘記釋放記憶體,造成記憶體洩露,有時在尚有指標...

動態記憶體與智慧型指標

智慧型指標與常規指標的區別在於它自動釋放所指向的物件。shared ptr 允許多個指標指向同乙個物件 unique ptr 獨佔 所指向的物件。weak ptr 伴隨類,弱引用,指向shared ptr所管理的物件。定義,與vector類似,智慧型指標也是模板 shared ptr p1 shar...