C 智慧型指標之shared ptr

2021-07-02 15:25:13 字數 4483 閱讀 1741

c++中用new來動態分配記憶體,delete手動釋放記憶體來達到動態管理記憶體的目的。因為保證在正確的時間釋放記憶體是非常困難的,忘記釋放記憶體就會產生記憶體洩露。

為了更安全、便捷的使用動態記憶體,c++11標準庫提供了新的智慧型指標類來管理動態記憶體。智慧型指標在行為上和普通的指標是一樣的,只不過它可以保證內存在適當的時候自動釋放。

新的標準庫提供了兩種智慧型指標(在標頭檔案中),區別在於管理底層指標的方式。

類似vector,智慧型指標也是一種模板,因此建立智慧型指標時,我們需要提供額外的資訊表明指標指向的型別。

shared_ptrp1;//建立乙個空指標p1,可以指向int型別

shared_ptr> p2;//建立乙個空指標p2,可以指向list型別

這僅僅是建立了乙個智慧型指標,並沒有給它分配記憶體。

通常有兩種方式給shared_ptr分配記憶體

最安全的分配和使用動態內部的方式是呼叫名為make_shared的標準庫函式。此函式在動態記憶體中分配乙個物件並初始化它,返回指向此物件的shared_ptr。

shared_ptr

p1 = make_shared(42);

//建立指向int的shard_ptr並且用42初始化

shared_ptr

p2 = make_shared(10, '9');

//建立乙個指向string的shared_ptr並且呼叫string的(n,char)建構函式初始化

shared_ptr

p3 = make_shared();

//建立指向my_class的shared_ptr並且呼叫my_class的預設建構函式初始化

類似於容器操作的emplace,make_shared函式用其引數來構造給定型別的物件。所以說如果是構造乙個類的shared_ptr,一定要注意初始化時必須和某個建構函式匹配

前邊提到過,shared_ptr允許多個指標指向同乙個物件。所以我們可以認為每乙個shared_ptr都有乙個和它關聯的計數器,稱為引用計數器。當滿足下述條件時,引用計數器遞增。

shared_ptr

p1 = make_shared(42);

printcount(p1);

void printcount(shared_ptr

p)

輸出結果是2,因為p1被當做了引數傳遞

shared_ptr

> getvector()

shared_ptr

> p = getvector();

cout

<< p.use_count() << endl;

輸出結果是1,雖然初始化的時候沒有顯式的用make_shared函式給p分配記憶體,但是呼叫了getvector()函式,返回了乙個shard_ptr,這樣p指向的物件引用計數器增加。

當乙個物件的引用計數減為0後,shared_ptr會自動銷毀物件,釋放記憶體。

當以下兩種情況發生時,shared_ptr的引用計數會遞減

auto p1 = make_shared(42);

p1 = p2;

給p1賦值使它指向p2指向的物件,會遞增p2指向的物件的引用計數,遞減p1指向物件的引用計數,此時p1指向物件的引用計數為0,被自動銷毀。

void printcount(shared_ptr

p)

x是區域性shared_ptr,當函式結束時,x指向的物件引用計數減為0,x指向的物件被銷毀,釋放占用的記憶體。

如前所說,如果不初始化乙個智慧型指標,那麼它就會被初始化為乙個空指標。除了用make_shared函式我們還可以用new返回指標初始化智慧型指標。

例如shared_ptrp(new int(42));

該語句實際上呼叫了智慧型指標的建構函式,該建構函式的引數是普通指標。這種建構函式是顯式的,因此shared_ptrp = new int(42);這種方式是錯誤的。

同樣的理由,乙個返回shared_ptr的函式不能在其返回語句中隱式轉換乙個普通指標。

shared_ptr

clone(int p)

正確寫法應當是:

shared_ptr

clone(int p)

還有,必須要強調的一點是:

預設情況下,乙個用來初始化shared_ptr的普通指標必須指向動態分配的記憶體,這是因為智慧型指標預設用delete釋放它關聯的記憶體

如果出於一些目的,必須用不指向動態分配記憶體的指標來初始化智慧型指標,就必須自定義一種釋放記憶體的方式(可以理解為過載delete)。

詳細例子在3.shared_ptr支援的操作中介紹。

操作解釋

shared_ptr< t > sp

建立空智能指標,可以指向t型別的物件

sp.get()

返回sp中儲存的指標。ps:一定要小心使用,如果智慧型指標釋放了物件,返回的指標指向的物件也將消失

sp.reset()

若sp是唯一指向該物件的shared_ptr,reset釋放該物件

sp.reset(p)

sp不在指向原來它指向的物件,指向內建指標p指向的物件,這裡p是被new動態分配記憶體的

sp.reset(p,d)

sp不在指向原來它指向的物件,指向內建指標p指向的物件,這裡p將會被可呼叫物件d釋放

sp.use_count()

返回sp指向物件的引用計數

sp.unique()

如果sp.use_count() == 1,返回true否則返回false

swap(sp1,sp2)

交換兩個智慧型指標

shared_ptr< t > sp(p)

sp管理內建指標p,p必須是被new動態分配記憶體的,而且能轉換為t*型別

shared_ptr< t > sp(p,d)

sp接管內建指標p指向物件的所有權,p必須能准換為t*型別,sp將使用可呼叫物件d代替delete

shared_ptr< t > sp1(sp2,d)

sp1是shared_ptr sp2的拷貝,使用可呼叫物件d代替delete

上述操作表中,引入了一種新的做法,使用可呼叫物件d來釋放內建指標

這基於這樣的假設:

內建指標p指向的物件不是由new動態分配的,然而我們還是用了p初始化了sp(shared_ptr),此時就必須用「過載」delete函式,順利釋放p的記憶體。

這樣是很麻煩的,具體的例子可以看這裡:

c++ primer 第五版 練習12.4

假如說存在函式:

void process(shared_ptr

ptr)

//ptr離開作用域,被ptr被銷毀,但是它指向的記憶體不會被釋放

此函式的正確使用方法傳遞shared_ptr:

shared_ptr

ptr(new

int(42));

//ptr指向物件引用計數為1

process(ptr);

//process中ptr指向的物件引用計數為2

int i = *ptr;

//正確,引用計數為2

典型的錯誤用法如下:

int * x(new

int(1024));

process(x);

//錯誤,內建指標不能隱式的轉換為shared_ptr

process(shared_ptr

(x));

//正確,process中ptr.use_count() == 1,process結束時ptr指向的記憶體會被釋放!

int i = *x;

//未定義行為,x是空懸指標!

首先,語句shared_ptr(x)相當於乙個強制轉換,得到乙個臨時變數shared_ptr,呼叫process時,相當於void process(shared_ptrptr(x)){},那麼當process結束的時候,ptr指向的物件的引用計數變為0,記憶體被釋放,也就是x指標指向的記憶體被釋放,x變為了乙個空懸指標。

記住:當shared_ptr繫結到內建指標時。我們就把記憶體的管理責任教給了shared_ptr,不要再用內建指標訪問該記憶體。

出於近乎相同的理由,不要使用get返回的內建指標初始化其他的智慧型指標!

C 智慧型指標之shared ptr

shared ptr 是為了解決 auto ptr 在物件所有權上的侷限性 auto ptr 是獨佔的 在使用引用計數的機制上提供了可以共享所有權的智慧型指標,當然這需要額外的開銷 1 shared ptr 物件除了包括乙個所擁有物件的指標外,還必須包括乙個引用計數 物件的指標。2 時間上的開銷主要...

智慧型指標之shared ptr

在智慧型指標中,auto ptr已經使用的越來越少了,特別是在c 11中,它已經被shared ptr取代,原因主要有以下兩點 1.auto ptr不能保留指標所有權 class test test public int m test void fun auto ptrp1 void main 上述...

c 智慧型指標 shared ptr

為了更容易更方便的使用動態記憶體,c 提供了兩種智慧型指標shared ptr和unique ptr來管理動態物件,智慧型指標負責自動釋放所管理的物件,避免因為不合適的釋放導致的記憶體洩露和訪問已釋放記憶體的bug 智慧型指標也是模板,建立乙個智慧型指標時,必須提供額外的型別資訊,如shared p...