C++11智慧指標用法

Owl丶發表於2020-10-05

shared_ptr和unique_ptr都支援的操作:

*p 
p->mem
p.get() 返回p中儲存的指標,智慧指標p在某些情況下可以代替p.get(),比如cout<<p 輸出內部指標
p.reset() 放棄內部物件的所有權,等價於 p = nullptr
p.reset(T *ptr) 變更擁有的物件
在shared中,reset會造成引用計數的減少,為0時刪除;unique中直接刪除

swap(p, q) 交換p和q中的指標

shared_ptr

建立

shared_ptr<T> p1;					//預設初始化,儲存空指標
shared_ptr<T> p1(T* ptr);			//建構函式,示例如下
shared_ptr<T> p1(new T());

shared_ptr<T> p2 = make_shared<T>(args);
shared_ptr<T> p3(p2);				//拷貝構造,會增加p2中的計數器

//管理陣列,自動呼叫delete[]而不是delete
shared_ptr<T[]> up(new T[10]);

//不使用原本的delete釋放操作,自定義刪除器:
shared_ptr<T> p3(..., deleterFun);	//定製void(T*)型別刪除器函式,或者函式物件或者lambda表示式

不能將普通指標直接賦值給智慧指標。

操作

p = q  p和q都是shared_ptr,所儲存的指標必須能相互轉換。遞減p的引用計數,若為0則釋放;遞增q的引用計數。
p.unique() 返回是否是獨佔所有權
p.use_count() 返回引用計數

注意事項

不要使用同一個原始指標構造 shared_ptr:

    int *num = new int(23);
    std::shared_ptr<int> p1(num);
    
    std::shared_ptr<int> p2(p1);  // 正確使用方法
    std::shared_ptr<int> p3(num); // 不推薦

    std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 輸出 2
    std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 輸出 2
    std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 輸出 1

雖然p1 p3指向相同物件,但並沒有統一,當p1超出作用域時會呼叫delete釋放num記憶體,此時num成了懸空指標,當p3超出作用域再次delete的時候就可能會出錯。

weak_ptr

weak_ptr 是為了配合shared_ptr而引入的一種智慧指標,對被 shared_ptr 管理的物件存在非擁有性(“弱”)引用,無法直接訪問,也不會影響引用計數。在訪問所引用的物件前必須先轉換為 shared_ptr。

weak_ptr<T> w    預設構造,空指標
weak_ptr<T> w(p) p為shared或weak,與p指向相同物件。T必須能轉換成p指向的型別
w = p 同上
w.reset()        將w置為空
w.use_count()    w監視物件的引用計數
w.expired()      w指向的物件引用計數是否為0,是為true
w.lock()         如果expired為true,返回空shared_ptr;否則返回一個指向w的shared_ptr

weak_ptr 用來表達臨時所有權的概念:當某個物件只有存在時才需要被訪問,而且隨時可能被他人刪除時,可以使用 weak_ptr 來跟蹤該物件。需要獲得臨時所有權時,則將其轉換為 shared_ptr(計數+1)。

if(shared_ptr<int> np = wp.lock()){ //如果np不為空則條件成立,計數暫時+1
    // 在if中,np與p共享物件
}
//退出if函式體,計數-1恢復

weak_ptr 的另一用法是打斷 shared_ptr 所管理的物件組成的環狀引用。若這種環被孤立(例如無指向環中的外部共享指標),則 shared_ptr 引用計數無法抵達零,而記憶體被洩露。能令環中的指標之一為弱指標以避免此情況。

unique_ptr

unique_ptr<T> u1	//預設構造,儲存空指標


unique_ptr<T> up(T *ptr)
unique_ptr<T> up(new T)

//自定義刪除器,和shared_ptr不同,分別代表兩種不同的刪除器繫結方式:《C++primer 5th》P599
//1.shared_ptr在執行時繫結刪除器,刪除器儲存為一個指標,可以隨時通過reset來更改刪除器型別,更靈活;
//2.unique_ptr在編譯時繫結,因為刪除器的型別是unique_ptr型別的一部分,更高效。
unique_ptr<T, D> u2(new T)		//用一個型別為D的可呼叫物件來代替delete
unique_ptr<T, D> u2(new T, d) 	//用型別為D的物件d代替delete
unique_ptr<T, decltype(deleterFun)*> u(dnew T, eleterFun)	//使用刪除器函式,lambda同理
unique_ptr<T> up = make_unique<T>(args)	//C++14起用

u.release()		//返回指標,放棄控制權(並不釋放記憶體),將u置空,不同於reset

自定義刪除器:https://blog.csdn.net/DumpDoctorWang/article/details/88598015

unique_ptr獨佔指向的物件,不能拷貝或賦值unique_ptr,但可以通過呼叫release或reset轉移指標所有權:

unique_ptr<A> p(new A);
unique_ptr<A> p2(p.release());
unique_ptr<A> p3;
p3.reset(p2.release());

傳遞unique_ptr引數和返回unique_ptr

不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷燬的unique_ptr。

unique_ptr<int> clone(int p){
    unique_ptr<int> ret(new int(p));
    //...
    return ret;
}

編譯器知道要返回的物件將要被銷燬。在此情況下,編譯器執行移動賦值。

參考:
https://zh.cppreference.com/w/cpp/memory/unique_ptr
https://blog.csdn.net/DumpDoctorWang/article/details/88598015
https://blog.csdn.net/shaosunrise/article/details/85228823?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
《C++primer 5th》

相關文章