std::enable_shared_from_this的原理及意義

原文:https://www.cnblogs.com/lehoho/p/9372195.html 和 https://www.jianshu.com/p/4444923d79bd

enable_shared_from_this是一個模板類,定義於頭文件<memory>,其原型爲:

template< class T > class enable_shared_from_this;

       std::enable_shared_from_this 能讓一個對象(假設其名爲 t ,且已被一個 std::shared_ptr 對象 pt 管理)安全地生成其他額外的 std::shared_ptr 實例(假設名爲 pt1, pt2, ... ) ,它們與 pt 共享對象 t 的所有權。
       若一個類 T 繼承 std::enable_shared_from_this<T> ,則會爲該類 T 提供成員函數: shared_from_this 。 當 T 類型對象 t 被一個爲名爲 pt 的 std::shared_ptr<T> 類對象管理時,調用 T::shared_from_this 成員函數,將會返回一個新的 std::shared_ptr<T> 對象,它與 pt 共享 t 的所有權。

一.使用場合

       當類A被share_ptr管理,且在類A的成員函數裏需要把當前類對象作爲參數傳給其他函數時,就需要傳遞一個指向自身的share_ptr。

1.爲何不直接傳遞this指針

       使用智能指針的初衷就是爲了方便資源管理,如果在某些地方使用智能指針,某些地方使用原始指針,很容易破壞智能指針的語義,從而產生各種錯誤。

2.可以直接傳遞share_ptr<this>麼?

       答案是不能,因爲這樣會造成2個非共享的share_ptr指向同一個對象,未增加引用計數導對象被析構兩次

 

例如:

#include <memory>
#include <iostream>

class Bad
{
public:
    std::shared_ptr<Bad> getptr() {
        return std::shared_ptr<Bad>(this);
    }
~Bad() { 
    std::cout << "Bad::~Bad() called" << std::endl; 
}
};

int main()
{
    // 錯誤的示例,每個shared_ptr都認爲自己是對象僅有的所有者
    std::shared_ptr<Bad> bp1(new Bad());
    std::shared_ptr<Bad> bp2 = bp1->getptr();
    // 打印bp1和bp2的引用計數
    std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
    std::cout << "bp2.use_count() = " << bp2.use_count() << std::endl;
} // Bad 對象將會被刪除兩次
  1. 輸出結果如下:


當然,一個對象被刪除兩次會導致崩潰。

正確的實現如下:

#include <memory>
#include <iostream>

struct Good : std::enable_shared_from_this<Good> // 注意:繼承
{
public:
    std::shared_ptr<Good> getptr() {
    return shared_from_this();
}
~Good() { 
    std::cout << "Good::~Good() called" << std::endl;
}
};

int main()
{
// 大括號用於限制作用域,這樣智能指針就能在system("pause")之前析構
    {
        std::shared_ptr<Good> gp1(new Good());
        std::shared_ptr<Good> gp2 = gp1->getptr();
        // 打印gp1和gp2的引用計數
        std::cout << "gp1.use_count() = " << gp1.use_count() << std::endl;
        std::cout << "gp2.use_count() = " << gp2.use_count() << std::endl;
    }
    system("pause");
}

輸出結果如下:

二.爲何會出現這種使用場合

       因爲在異步調用中,存在一個保活機制,異步函數執行的時間點我們是無法確定的,然而異步函數可能會使用到異步調用之前就存在的變量。爲了保證該變量在異步函數執期間一直有效,我們可以傳遞一個指向自身的share_ptr給異步函數,這樣在異步函數執行期間share_ptr所管理的對象就不會析構,所使用的變量也會一直有效了(保活)。

具體的應用可以參考:Boost.Asio C++ 網絡編程之五:TCP回顯服務端/客戶端

 

另一篇文章:

首先看cppreference的解釋:

 

std::enable_shared_from_this allows an object t that is currently managed by a std::shared_ptr named pt to safely generate additional std::shared_ptr instances pt1, pt2, ... that all share ownership of t with pt.Publicly inheriting from std::enable_shared_from_this provides the type T with a member function shared_from_this. If an object t of type T is managed by a std::shared_ptr named pt, then calling T::shared_from_this will return a new std::shared_ptr that shares ownership of t with pt

意思就是說,如果一個T類型的對象t,是被std::shared_ptr管理的,且類型T繼承自std::enable_shared_from_this,那麼T就有個shared_from_this的成員函數,這個函數返回一個新的std::shared_ptr的對象,也指向對象t。

    那麼這個特性的應用場景是什麼呢?一個主要的場景是保證異步回調函數中操作的對象仍然有效。比如有這樣一個類:

Foo::Bar接受一個函數對象,這個對象需要一個Foo*指針,其實要的就是Foo::Bar的this指針,但是這個回調是異步的,也就是說可能在調用這個回調函數時,this指向的Foo對象已經提前析構了。這時候,std::enable_shared_from_this就派上用場了。修改後如下:

這樣就可以保證異步回調時,Foo對象仍然有效。

    注意到cppreference中說道,必須要是std::shared_ptr管理的對象,調用shared_from_this纔是有效的,爲什麼呢?這個就需要看看std::enable_shared_from_this的實現原理了:

    std::enable_shared_from_this<T> 有一個std::weak_ptr<T>的成員,實際上在構造std::enable_shared_from_this<T>時,並沒有初始化std::weak_ptr<T>成員,而是在用這個std::enable_shared_from_this<T>去構造std::shared_ptr的時候,去構造並初始化這個std::weak_ptr<T>成員。所以這也就是爲什麼cppreference中說的這個對象必須是std::shared_ptr管理的,因爲這個對象不是通過std::shared_ptr來管理,那麼std::weak_ptr是未初始化的,無法通過其提升爲std::shared_ptr對象。OK,原理說了,具體實現可以去看代碼,不瞭解std::weak_ptr和std::shared_ptr的關係的,可以看看這篇文章:關於shared_ptr的幾個問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章