c++11新增了新的智慧型指標,unique_ptr、shared_ptr和weak_ptr,同時也將auto_ptr置為廢棄(deprecated)。
但是在實際的使用過程中,很多人都會有這樣的問題:
不知道三種智慧型指標的具體使用場景
無腦只使用shared_ptr
認為應該禁用raw pointer(裸指標,即widget*這種形式),全部使用智慧型指標
初始化方法12
3456
78910
1112
1314
1516
1718
1920
2122
2324
class a
a(){}
void show()
private:
int size = 5;
};...
//[1]
auto p1 = std::make_shared();
auto p2 = std::make_shared();
//[2]
std::shared_ptrp3(new int(5));
std::shared_ptrp4(new a());
//[3]
std::shared_ptrp5;
p5.reset(new int(5));
std::shared_ptrp6;
p6.reset(new a());
推薦使用第一種方法~
使用場景
unique_ptr
忘記delete12
3456
78910
11class box
~box()
private:
widget* w;
};異常安全12
3456
void process()
shared_ptr
shared_ptr通常使用在共享權不明的場景。有可能多個物件同時管理同乙個記憶體時。
物件的延遲銷毀。陳碩在《linux多執行緒伺服器端程式設計》中提到,當乙個物件的析構非常耗時,甚至影響到了關鍵執行緒的速度。可以使用blockingqueue>將物件轉移到另外乙個執行緒中釋放,從而解放關鍵執行緒。
weak_ptr
weak_ptr是為了解決shared_ptr雙向引用的問題。即:12
3456
78910
11class b;
struct a;
struct b;
auto pa = make_shared();
auto pb = make_shared();
pa->b = pb;
pb->a = pa;
pa和pb存在著迴圈引用,根據shared_ptr引用計數的原理,pa和pb都無法被正常的釋放。
對於這種情況, 我們可以使用weak_ptr:12
3456
78910
11class b;
struct a;
struct b;
auto pa = make_shared();
auto pb = make_shared();
pa->b = pb;
pb->a = pa;
weak_ptr不會增加引用計數,因此可以打破shared_ptr的迴圈引用。
通常做法是parent類持有child的shared_ptr, child持有指向parent的weak_ptr。這樣也更符合語義。
效能1.unique_ptr
因為c++的zero cost abstraction的特點,unique_ptr在預設情況下和裸指標的大小是一樣的。
所以記憶體上沒有任何的額外消耗,效能是最優的。
2.shared_ptr
存占用高 shared_ptr的記憶體占用是裸指標的兩倍。因為除了要管理乙個裸指標外,還要維護乙個引用計數。 因此相比於unique_ptr, shared_ptr的記憶體占用更高
原子操作效能低 考慮到執行緒安全問題,引用計數的增減必須是原子操作。而原子操作一般情況下都比非原子操作慢。
使用移動優化效能 shared_ptr在效能上固然是低於unique_ptr。而通常情況,我們也可以盡量避免shared_ptr複製。 如果,乙個shared_ptr需要將所有權共享給另外乙個新的shared_ptr,而我們確定在之後的**中都不再使用這個shared_ptr,那麼這是乙個非常鮮明的移動語義。 對於此種場景,我們盡量使用std::move,將shared_ptr轉移給新的物件。因為移動不用增加引用計數,因此效能比複製更好。
物件所有權
首先需要理清楚的概念就是物件所有權的概念。所有權在rust語言中非常嚴格,寫rust的時候必須要清楚自己建立的每個物件的所有權。
但是c++比較自由,似乎我們不需要明白物件的所有權,寫的**也能正常執行。但是明白了物件所有權,我們才可以正確管理好物件生命週期和記憶體問題。
c++引入了智慧型指標,也是為了更好的描述物件所有權,簡化記憶體管理,從而大大減少我們c++記憶體管理方面的犯錯機會。
unique_ptr
我們大多數場景下用到的應該都是unique_ptr。
unique_ptr代表的是專屬所有權,即由unique_ptr管理的記憶體,只能被乙個物件持有。
所以,unique_ptr不支援複製和賦值,如下:12
auto w = std::make_unique();
auto w2 = w; // 編譯錯誤
如果想要把w複製給w2, 是不可以的。因為複製從語義上來說,兩個物件將共享同一塊記憶體。
因此,unique_ptr只支援移動, 即如下:12
auto w = std::make_unique();
auto w2 = std::move(w); // w2獲得記憶體所有權,w此時等於nullptr
unique_ptr代表的是專屬所有權,如果想要把乙個unique_ptr的記憶體交給另外乙個unique_ptr物件管理。只能使用std::move轉移當前物件的所有權。轉移之後,當前物件不再持有此記憶體,新的物件將獲得專屬所有權。
如上**中,將w物件的所有權轉移給w2後,w此時等於nullptr,而w2獲得了專屬所有權。
shared_ptr
在使用shared_ptr之前應該考慮,是否真的需要使用shared_ptr, 而非unique_ptr。
shared_ptr代表的是共享所有權,即多個shared_ptr可以共享同一塊記憶體。
因此,從語義上來看,shared_ptr是支援複製的。如下:12
3456
auto w = std::make_shared();
cout << w.use_count() << endl; // 1
shared_ptr內部是利用引用計數來實現記憶體的自動管理,每當複製乙個shared_ptr,引用計數會+1。當乙個shared_ptr離開作用域時,引用計數會-1。當引用計數為0的時候,則delete記憶體。
同時,shared_ptr也支援移動。從語義上來看,移動指的是所有權的傳遞。如下:12
auto w = std::make_shared();
auto w2 = std::move(w); // 此時w等於nullptr,w2.use_count()等於1
我們將w物件move給w2,意味著w放棄了對記憶體的所有權和管理,此時w物件等於nullptr。
而w2獲得了物件所有權,但因為此時w已不再持有物件,因此w2的引用計數為1。
指標作為函式傳參
只在函式使用指標,但並不儲存
假如我們只需要在函式中,用這個物件處理一些事情,但不打算涉及其生命週期的管理,不打算通過函式傳參延長shared_ptr的生命週期。
對於這種情況,可以使用raw pointer或者const shared_ptr&。即:1
2void func(widget*);
void func(const shared_ptr&)
實際上第一種裸指標的方式可能更好,從語義上更加清楚,函式也不用關心智慧型指標的型別。
在函式中儲存智慧型指標
假如我們需要在函式中把這個智慧型指標儲存起來,這個時候建議直接傳值。void func(std::shared_ptr ptr);這樣的話,外部傳過來值的時候,可以選擇move或者賦值。函式內部直接把這個物件通過move的方式儲存起來。 這樣效能更好,而且外部呼叫也有多種選擇。
C 11新特性之智慧型指標
這一節將從用法上 記憶體儲存上以及生存週期上,對unique ptr,shared ptr和weak ptr做乙個深入剖析。unique ptr 不共享它的指標。它無法複製到其他 unique ptr,無法通過值傳遞到函式,也無法用於需要副本的任何標準模板庫 stl 演算法。只能移動 unique ...
詳解C 11智慧型指標
目錄 詳解c 11智慧型指標 前言 c 11智慧型指標介紹 為什麼要使用智慧型指標 auto ptr unique ptr shared ptr weak ptr 有些c 特性並沒有帶來實際上效能的提公升,而且還要花很多的時間來學習,導致我一段時間並不看好智慧型指標,因為普通指標用的很習慣了。但是看...
C 面試 C 11 新特性之智慧型指標
shared ptr 基本用法 shared ptr 採用引用計數的方式管理所指向的物件 當有乙個新的shared ptr指向同乙個物件時 複製shared ptr等 引用計數加1。當shared ptr離開作用域時,引用計數減1。當引用計數為0時,釋放所管理的記憶體。這樣做的好處在於解放了程式設計...