https://m.toutiao.com/is/JHS4MVf/ 智能指針 C++11 引入了 3 個智能指針類型:
std::auto_ptr 已被廢棄。 std::unique_ptr簡單說,當(dāng)我們獨(dú)占資源的所有權(quán)的時候,可以使用 std::unique_ptr 對資源進(jìn)行管理——離開 unique_ptr 對象的作用域時,會自動釋放資源。這是很基本的 RAII 思想。 std::unique_ptr 的使用比較簡單,也是用得比較多的智能指針。這里直接看例子。
{ int* p = new int(100); // ... delete p; // 要記得釋放內(nèi)存}
{ std::unique_ptr<int> uptr = std::make_unique<int>(200); std::unique_ptr<int> uptr1 = uptr; // 編譯錯誤,std::unique_ptr<T> 是 move-only 的 std::unique_ptr<int> uptr2 = std::move(uptr); assert(uptr == nullptr);}
{ struct FileCloser { void operator()(FILE* fp) const { if (fp != nullptr) { fclose(fp); } } }; std::unique_ptr<FILE, FileCloser> uptr(fopen('test_file.txt', 'w'));}
std::shared_ptrstd::shared_ptr 其實(shí)就是對資源做引用計數(shù)——當(dāng)引用計數(shù)為 0 的時候,自動釋放資源。 { std::shared_ptr<int> sptr = std::make_shared<int>(200); assert(sptr.use_count() == 1); // 此時引用計數(shù)為 1 { std::shared_ptr<int> sptr1 = sptr; assert(sptr.get() == sptr1.get()); assert(sptr.use_count() == 2); // sptr 和 sptr1 共享資源,引用計數(shù)為 2 } assert(sptr.use_count() == 1); // sptr1 已經(jīng)釋放}// use_count 為 0 時自動釋放內(nèi)存 和 unique_ptr 一樣,shared_ptr 也可以指向數(shù)組和自定義 deleter。
std::shared_ptr 的實(shí)現(xiàn)原理一個 shared_ptr 對象的內(nèi)存開銷要比裸指針和無自定義 deleter 的 unique_ptr 對象略大。 std::cout << sizeof(int*) << std::endl; // 輸出 8std::cout << sizeof(std::unique_ptr<int>) << std::endl; // 輸出 8std::cout << sizeof(std::unique_ptr<FILE, std::function<void(FILE*)>>) << std::endl; // 輸出 40std::cout << sizeof(std::shared_ptr<int>) << std::endl; // 輸出 16std::shared_ptr<FILE> sptr(fopen('test_file.txt', 'w'), [](FILE* fp) { std::cout << 'close ' << fp << std::endl; fclose(fp);}); std::cout << sizeof(sptr) << std::endl; // 輸出 16 無自定義 deleter 的 unique_ptr 只需要將裸指針用 RAII 的手法封裝好就行,無需保存其它信息,所以它的開銷和裸指針是一樣的。如果有自定義 deleter,還需要保存 deleter 的信息。 shared_ptr 需要維護(hù)的信息有兩部分:
所以,shared_ptr 對象需要保存兩個指針。shared_ptr 的 的 deleter 是保存在控制信息中,所以,是否有自定義 deleter 不影響 shared_ptr 對象的大小。 當(dāng)我們創(chuàng)建一個 shared_ptr 時,其實(shí)現(xiàn)一般如下:
復(fù)制一個 shared_ptr : std::shared_ptr<T> sptr2 = sptr1; 為什么控制信息和每個 shared_ptr 對象都需要保存指向共享資源的指針?可不可以去掉 shared_ptr 對象中指向共享資源的指針,以節(jié)省內(nèi)存開銷? 答案是:不能。因為 shared_ptr 對象中的指針指向的對象不一定和控制塊中的指針指向的對象一樣。 來看一個例子。
另外,std::shared_ptr 支持 aliasing constructor。 template< class Y >shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept; Aliasing constructor,簡單說就是構(gòu)造出來的 shared_ptr 對象和參數(shù) r 指向同一個控制塊(會影響 r 指向的資源的生命周期),但是指向共享資源的指針是參數(shù) ptr。看下面這個例子。
看上面的例子,使用 std::shared_ptr 時,會涉及兩次內(nèi)存分配:一次分配共享資源對象;一次分配控制塊。C++ 標(biāo)準(zhǔn)庫提供了 std::make_shared 函數(shù)來創(chuàng)建一個 shared_ptr 對象,只需要一次內(nèi)存分配。 這種情況下,不用通過控制塊中的指針,我們也能知道共享資源的位置——這個指針也可以省略掉。 std::weak_ptrstd::weak_ptr 要與 std::shared_ptr 一起使用。一個 std::weak_ptr 對象看做是 std::shared_ptr 對象管理的資源的觀察者,它不影響共享資源的生命周期:
void Observe(std::weak_ptr<int> wptr) { if (auto sptr = wptr.lock()) { std::cout << 'value: ' << *sptr << std::endl; } else { std::cout << 'wptr lock fail' << std::endl; }}std::weak_ptr<int> wptr;{ auto sptr = std::make_shared<int>(111); wptr = sptr; Observe(wptr); // sptr 指向的資源沒被釋放,wptr 可以成功提升為 shared_ptr}Observe(wptr); // sptr 指向的資源已被釋放,wptr 無法提升為 shared_ptr 當(dāng) shared_ptr 析構(gòu)并釋放共享資源的時候,只要 weak_ptr 對象還存在,控制塊就會保留,weak_ptr 可以通過控制塊觀察到對象是否存活。 enable_shared_from_this一個類的成員函數(shù)如何獲得指向自身(this)的 shared_ptr?看看下面這個例子有沒有問題?
上面的代碼其實(shí)會生成兩個獨(dú)立的 shared_ptr,他們的控制塊是獨(dú)立的,最終導(dǎo)致一個 Foo 對象會被 delete 兩次。 成員函數(shù)獲取 this 的 shared_ptr 的正確的做法是繼承 std::enable_shared_from_this 。 class Bar : public std::enable_shared_from_this<Bar> { public: std::shared_ptr<Bar> GetSPtr() { return shared_from_this(); }};auto sptr1 = std::make_shared<Bar>();assert(sptr1.use_count() == 1);auto sptr2 = sptr1->GetSPtr();assert(sptr1.use_count() == 2);assert(sptr2.use_count() == 2); 一般情況下,繼承了 std::enable_shared_from_this 的子類,成員變量中增加了一個指向 this 的 weak_ptr。這個 weak_ptr 在第一次創(chuàng)建 shared_ptr 的時候會被初始化,指向 this。 似乎繼承了 std::enable_shared_from_this 的類都被強(qiáng)制必須通過 shared_ptr 進(jìn)行管理。
在我的環(huán)境下(gcc 7.5.0)上面的代碼執(zhí)行的時候會直接 coredump,而不是返回指向 nullptr 的 shared_ptr: terminate called after throwing an instance of 'std::bad_weak_ptr' what(): bad_weak_ptr 小結(jié)智能指針,本質(zhì)上是對資源所有權(quán)和生命周期管理的抽象:
參考資料
|
|