国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
現(xiàn)代C一文讀懂智能指針

https://m.toutiao.com/is/JHS4MVf/ 



智能指針

C++11 引入了 3 個(gè)智能指針類型:

  1. std::unique_ptr<T> :獨(dú)占資源所有權(quán)的指針。
  2. std::shared_ptr<T> :共享資源所有權(quán)的指針。
  3. std::weak_ptr<T> :共享資源的觀察者,需要和 std::shared_ptr 一起使用,不影響資源的生命周期。

std::auto_ptr 已被廢棄。

std::unique_ptr

簡(jiǎn)單說,當(dāng)我們獨(dú)占資源的所有權(quán)的時(shí)候,可以使用 std::unique_ptr 對(duì)資源進(jìn)行管理——離開 unique_ptr 對(duì)象的作用域時(shí),會(huì)自動(dòng)釋放資源。這是很基本的 RAII 思想。

std::unique_ptr 的使用比較簡(jiǎn)單,也是用得比較多的智能指針。這里直接看例子。

  1. 使用裸指針時(shí),要記得釋放內(nèi)存。
{ int* p = new int(100); // ... delete p; // 要記得釋放內(nèi)存}
  1. 使用 std::unique_ptr 自動(dòng)管理內(nèi)存。
{    std::unique_ptr<int> uptr = std::make_unique<int>(200);    //...    // 離開 uptr 的作用域的時(shí)候自動(dòng)釋放內(nèi)存}
  1. std::unique_ptr 是 move-only 的。
{ std::unique_ptr<int> uptr = std::make_unique<int>(200); std::unique_ptr<int> uptr1 = uptr; // 編譯錯(cuò)誤,std::unique_ptr<T> 是 move-only 的 std::unique_ptr<int> uptr2 = std::move(uptr); assert(uptr == nullptr);}
  1. std::unique_ptr 可以指向一個(gè)數(shù)組。
{    std::unique_ptr<int[]> uptr = std::make_unique<int[]>(10);    for (int i = 0; i < 10; i++) {        uptr[i] = i * i;    }       for (int i = 0; i < 10; i++) {        std::cout << uptr[i] << std::endl;    }   }
  1. 自定義 deleter。
{ struct FileCloser { void operator()(FILE* fp) const { if (fp != nullptr) { fclose(fp); } } }; std::unique_ptr<FILE, FileCloser> uptr(fopen('test_file.txt', 'w'));}
  1. 使用 Lambda 的 deleter。
{    std::unique_ptr<FILE, std::function<void(FILE*)>> uptr(        fopen('test_file.txt', 'w'), [](FILE* fp) {            fclose(fp);        });}

std::shared_ptr

std::shared_ptr 其實(shí)就是對(duì)資源做引用計(jì)數(shù)——當(dāng)引用計(jì)數(shù)為 0 的時(shí)候,自動(dòng)釋放資源。

{ std::shared_ptr<int> sptr = std::make_shared<int>(200); assert(sptr.use_count() == 1); // 此時(shí)引用計(jì)數(shù)為 1 { std::shared_ptr<int> sptr1 = sptr; assert(sptr.get() == sptr1.get()); assert(sptr.use_count() == 2); // sptr 和 sptr1 共享資源,引用計(jì)數(shù)為 2 } assert(sptr.use_count() == 1); // sptr1 已經(jīng)釋放}// use_count 為 0 時(shí)自動(dòng)釋放內(nèi)存

和 unique_ptr 一樣,shared_ptr 也可以指向數(shù)組和自定義 deleter。

{    // C++20 才支持 std::make_shared<int[]>    // std::shared_ptr<int[]> sptr = std::make_shared<int[]>(100);    std::shared_ptr<int[]> sptr(new int[10]);    for (int i = 0; i < 10; i++) {        sptr[i] = i * i;    }       for (int i = 0; i < 10; i++) {        std::cout << sptr[i] << std::endl;    }   }{    std::shared_ptr<FILE> sptr(        fopen('test_file.txt', 'w'), [](FILE* fp) {            std::cout << 'close ' << fp << std::endl;            fclose(fp);        });}

std::shared_ptr 的實(shí)現(xiàn)原理

一個(gè) shared_ptr 對(duì)象的內(nèi)存開銷要比裸指針和無(wú)自定義 deleter 的 unique_ptr 對(duì)象略大。

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

無(wú)自定義 deleter 的 unique_ptr 只需要將裸指針用 RAII 的手法封裝好就行,無(wú)需保存其它信息,所以它的開銷和裸指針是一樣的。如果有自定義 deleter,還需要保存 deleter 的信息。

shared_ptr 需要維護(hù)的信息有兩部分:

  1. 指向共享資源的指針。
  2. 引用計(jì)數(shù)等共享資源的控制信息——實(shí)現(xiàn)上是維護(hù)一個(gè)指向控制信息的指針。

所以,shared_ptr 對(duì)象需要保存兩個(gè)指針。shared_ptr 的 的 deleter 是保存在控制信息中,所以,是否有自定義 deleter 不影響 shared_ptr 對(duì)象的大小。

當(dāng)我們創(chuàng)建一個(gè) shared_ptr 時(shí),其實(shí)現(xiàn)一般如下:

std::shared_ptr<T> sptr1(new T);

復(fù)制一個(gè) shared_ptr :

std::shared_ptr<T> sptr2 = sptr1;

為什么控制信息和每個(gè) shared_ptr 對(duì)象都需要保存指向共享資源的指針?可不可以去掉 shared_ptr 對(duì)象中指向共享資源的指針,以節(jié)省內(nèi)存開銷?

答案是:不能。因?yàn)?shared_ptr 對(duì)象中的指針指向的對(duì)象不一定和控制塊中的指針指向的對(duì)象一樣。

來(lái)看一個(gè)例子。

struct Fruit {    int juice;};struct Vegetable {    int fiber;};struct Tomato : public Fruit, Vegetable {    int sauce;}; // 由于繼承的存在,shared_ptr 可能指向基類對(duì)象std::shared_ptr<Tomato> tomato = std::make_shared<Tomato>();std::shared_ptr<Fruit> fruit = tomato;std::shared_ptr<Vegetable> vegetable = tomato;

另外,std::shared_ptr 支持 aliasing constructor。

template< class Y >shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

Aliasing constructor,簡(jiǎn)單說就是構(gòu)造出來(lái)的 shared_ptr 對(duì)象和參數(shù) r 指向同一個(gè)控制塊(會(huì)影響 r 指向的資源的生命周期),但是指向共享資源的指針是參數(shù) ptr。看下面這個(gè)例子。

using Vec = std::vector<int>;std::shared_ptr<int> GetSPtr() {    auto elts = {0, 1, 2, 3, 4};    std::shared_ptr<Vec> pvec = std::make_shared<Vec>(elts);    return std::shared_ptr<int>(pvec, &(*pvec)[2]);}std::shared_ptr<int> sptr = GetSPtr();for (int i = -2; i < 3; ++i) {    printf('%d\n', sptr.get()[i]);}

看上面的例子,使用 std::shared_ptr 時(shí),會(huì)涉及兩次內(nèi)存分配:一次分配共享資源對(duì)象;一次分配控制塊。C++ 標(biāo)準(zhǔn)庫(kù)提供了 std::make_shared 函數(shù)來(lái)創(chuàng)建一個(gè) shared_ptr 對(duì)象,只需要一次內(nèi)存分配。

這種情況下,不用通過控制塊中的指針,我們也能知道共享資源的位置——這個(gè)指針也可以省略掉。

std::weak_ptr

std::weak_ptr 要與 std::shared_ptr 一起使用。一個(gè) std::weak_ptr 對(duì)象看做是 std::shared_ptr 對(duì)象管理的資源的觀察者,它不影響共享資源的生命周期:

  1. 如果需要使用 weak_ptr 正在觀察的資源,可以將 weak_ptr 提升為 shared_ptr。
  2. 當(dāng) shared_ptr 管理的資源被釋放時(shí),weak_ptr 會(huì)自動(dòng)變成 nullptr。
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 無(wú)法提升為 shared_ptr

當(dāng) shared_ptr 析構(gòu)并釋放共享資源的時(shí)候,只要 weak_ptr 對(duì)象還存在,控制塊就會(huì)保留,weak_ptr 可以通過控制塊觀察到對(duì)象是否存活。

enable_shared_from_this

一個(gè)類的成員函數(shù)如何獲得指向自身(this)的 shared_ptr?看看下面這個(gè)例子有沒有問題?

class Foo { public:  std::shared_ptr<Foo> GetSPtr() {    return std::shared_ptr<Foo>(this);  }};auto sptr1 = std::make_shared<Foo>();assert(sptr1.use_count() == 1);auto sptr2 = sptr1->GetSPtr();assert(sptr1.use_count() == 1);assert(sptr2.use_count() == 1);

上面的代碼其實(shí)會(huì)生成兩個(gè)獨(dú)立的 shared_ptr,他們的控制塊是獨(dú)立的,最終導(dǎo)致一個(gè) Foo 對(duì)象會(huì)被 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 的子類,成員變量中增加了一個(gè)指向 this 的 weak_ptr。這個(gè) weak_ptr 在第一次創(chuàng)建 shared_ptr 的時(shí)候會(huì)被初始化,指向 this。

似乎繼承了 std::enable_shared_from_this 的類都被強(qiáng)制必須通過 shared_ptr 進(jìn)行管理。

auto b = new Bar;auto sptr = b->shared_from_this();

在我的環(huán)境下(gcc 7.5.0)上面的代碼執(zhí)行的時(shí)候會(huì)直接 coredump,而不是返回指向 nullptr 的 shared_ptr:

terminate called after throwing an instance of 'std::bad_weak_ptr' what(): bad_weak_ptr

小結(jié)

智能指針,本質(zhì)上是對(duì)資源所有權(quán)和生命周期管理的抽象:

  1. 當(dāng)資源是被獨(dú)占時(shí),使用 std::unique_ptr 對(duì)資源進(jìn)行管理。
  2. 當(dāng)資源會(huì)被共享時(shí),使用 std::shared_ptr 對(duì)資源進(jìn)行管理。
  3. 使用 std::weak_ptr 作為 std::shared_ptr 管理對(duì)象的觀察者。
  4. 通過繼承 std::enable_shared_from_this 來(lái)獲取 this 的 std::shared_ptr 對(duì)象。

參考資料

  1. Back to Basics: Smart Pointers
  2. cppreference: unique_ptr
  3. cppreference: shared_ptr
  4. cppreference: weak_ptr
  5. cppreference: enable_shared_from_this
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C 11中智能指針的原理、使用、實(shí)現(xiàn)
智能指針shared
c 智能指針的使用
Boost.shared_ptr必須注意的一些地方
智能指針
C++11中的智能指針
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服