C++ Boost Library: shared_ptr

餘生
4 min readSep 11, 2021

--

Boost Library Version : 1.77.0

Difference with raw pointer

Raw pointer 在宣告的時候會分配新的記憶體位址,但 smart pointer 是用 call by reference 的方式,並且會有一個counter去紀錄有多少人參考這個位址,當計數器歸零的時候會自動釋放記憶體,可以減少programmer管理記憶體的負擔。

shared_ptr<int> p1(new int(5));
cout << p1.use_count() << endl; // 1
cout << p1.get() << endl; // 00000169F4620F80
shared_ptr<int> p2 = p1;
cout << p1.use_count() << endl; // 2
cout << p2.get() << endl; // 00000169F4620F80 (每次編譯執行都會不一樣,但p2和p1的地址一定一樣)
p1.reset();
p2.reset();
cout << p1.get() << endl; // 0000000000000000
cout << p2.get() << endl; // 0000000000000000

而 smart pointer 還有一個好處是,當被參考的變數或引數離開 function scope 後,此變數的count也會減少。這麼做可以減少發生 memory leak 的可能性,假如在 f() 分配了新的記憶體區塊 b,但是在 return 前忘記 free(b) 的話會造成我們沒辦法再對 b 操作,並且 b 一直占用著記憶體,久而久之就會發現記憶體開始不夠用,有可能間接導致 thrashing 現象。

值得注意的是,原本以為 f() 裡面的 ptr.user_count()會是2,但引數 ptr 是用 call by reference 的方式傳遞,所以ptr 加上 p_f 後再加上 main 裡面 p1 總共有3個地方參考了相同的位址。

void f(shared_ptr<int> ptr) {
shared_ptr<int> p_f(ptr);
cout << ptr.use_count() << endl; // 3
cout << ptr.get() << endl; // 00000169F4620F80
return;
}
int main()
{
shared_ptr<int> p1(new int(5));
cout << p1.use_count() << endl; // 1
cout << p1.get() << endl; // 00000169F4620F80
f(p1); cout << p1.use_count() << endl; // 1
cout << p1.get() << endl; // 00000169F4620F80
}

Memory Leak

除了上一段講到 smart pointer 可以避免因為程式設計錯誤而造成的 memory leak 外,在 boost document 也有提到建議傳遞引數的方式。盡量避免使用 bad() 的方法,compiler 會先分配記憶體空間給shared_ptr<int>( new int(2) )才接著執行 g(),若此時發生 exception 程式中斷,我們就沒辦法在繼續存取第一個參數了。反之,在 ok() 裡面,可以透過 p.reset() 釋放。

void f(shared_ptr<int>, int);
int g();

void ok()
{
shared_ptr<int> p( new int(2) );
f( p, g() );
}

void bad()
{
f( shared_ptr<int>( new int(2) ), g() );
}

上面只是其中一個的情況,可以參考這篇 GotW #56: Exception-Safe Function Calls 裡面有比較詳細的解釋。關於 memory leak 似乎有很大的篇幅可以探討,之後有空再來更新~(如果我還記得的話XD)

--

--

餘生
餘生

Written by 餘生

0 Followers

正在北大當菸酒生

No responses yet