• 何时/如何使用 std::enable_shared_from_this<T>?


    要点回顾


    • 继承自 std::enable_shared_from_this 的类能够在其自身实例中通过 std::shared_from_this 方法创建一个指向自己的 std::shared_ptr 智能指针。
    • 从一个裸指针创建多个 std::shared_ptr 实例会造成严重的后果,其行为是未定义的。
    • std::enable_shared_from_this 实际包含了一个用于指向对象自身的 std::weak_ptr 指针。

    引言


    本文介绍 std::enable_shared_from_thisstd::shared_from_this 的基本概念和使用方法。

    定义 "std::enable_shared_from_this"


    以下内容是 cppreference.com 上关于 std::enable_shared_from_this 的定义和说明:

    Defined in header < memory >
    template< class T > class enable_shared_from_this; (since C++11)

    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.

    简单来说就是,继承自 std::enable_shared_from_this 的类能够在其自身实例中通过 std::shared_from_this 方法创建一个指向自己的 std::shared_ptr 智能指针。

    想要理解 std::enable_shared_from_this,首先得知道为什么需要 std::enable_shared_from_this,请看下文。


    使用 "std::enable_shared_from_this"


    为什么需要 std::enable_shared_from_this? 我们从一个例子讲起,会更容易一些。

    假定有一个类 Processor, 它的作用是异步处理数据并且存储到数据库。当 Processor 接收到数据时,它通过一个定制的 Executor 类型来异步处理数据:

    class Executor {
    public:
     //Executes a task asynchronously
     void
     execute(const std::function<void(void)>& task);
     //....
    private:
     /* Contains threads and a task queue */
    };
    
    class Processor {
    public:
     //...
     //Processes data asynchronously. (implemented later)
     void processData(const std::string& data); 
    
    private:
     //Called in an Executor thread 
     void doProcessAndSave(const std::string& data) {
        //1. Process data
        //2. Save data to DB
     }
     //.. more fields including a DB handle..
     Executor* executor;
    };
    

    Client 类包含了一个 std::shared_ptr 实例,Processor 从 Client 类接收数据:

    class Client {
    public:
     void someMethod() {
      //...
      processor->processData("Some Data");
      //..do something else
     }
    private:
     std::shared_ptr processor;
    };
    

    以上示例中,Executor 是一个线程池,用于执行任务队列中的任务。
    Processor::processData 中,我们需要传递一个(指针)函数(lambda 函数)给 Executor 来执行异步操作。该 lambda 函数调用 Processor::doProcessAndSave 以完成实际的数据处理工作。因此,该 lambda 函数需要捕获一个 Processor 对象的引用/指针。我们可以这样做:

    void Processor::processData(const std::string& data) {
     executor->execute([this, data]() { //<--Bad Idea
       //Executes in an Executor thread asynchronously
       //'this' could be invalid here.
       doProcessAndSave(data);
     });
    }
    

    然而,因为种种原因,Client 可能会随时重置 std::shared_ptr,这可能导致 Processor 的实例被析构。因此,在执行 execute 函数时或者在执行之前,上述代码中捕获的 this 指针随时可能会变为无效指针。

    怎么办?

    我们可以通过在 lambda 函数中捕获并保留一个指向当前对象本身的 std::shared_ptr 实例来防止 Processor 对象被析构。

    下图展示了示例代码的交互逻辑:

    那么,在 Processor 实例中通过 shared_ptr(this) 创建一个智能指针呢?其行为是未定义的!

    std::shared_ptr 允许我们安全地访问和管理对象的生命周期。多个 std::shared_ptr 实例通过一个共享控制块结构(a shared control block structure)来管理对象的生命周期。一个控制块维护了一个引用计数,及其他必要的对象本身的信息。

    void good() {
     auto p{new int(10)}; //p is int*
     std::shared_ptr<int> sp1{p}; 
     //Create additional shared_ptr from an existing shared_ptr
     auto sp2{sp1}; //OK. sp2 shares control block with sp1
    }
    

    从一个裸指针创建一个 std::shared_ptr 会创建一个新的控制块。从一个裸指针创建多个 std::shared_ptr 实例会造成严重的后果:

    void bad() {
     auto p{new int(10)};   
     std::shared_ptr<int> sp1{p};
     std::shared_ptr<int> sp2{p}; //! Undefined Behavior
    }
    

    因此,我们需要一个机制能够达到我们的目的(捕获并保留一个指向当前对象本身的 std::shared_ptr 实例)。

    这就是 std::enable_shared_from_this 存在的意义,以下是修改后的类 Processor 实现:

    class Processor : public std::enable_shared_from_this {
     //..same as above...
    }; 
    
    void Processor::processData(const std::string& data) {
     //shared_from_this() returns a shared_ptr 
     //  to this Processor
     executor->execute([self = shared_from_this(), data]() { 
       //Executes in an Executor thread asynchronously
       //'self' is captured shared_ptr
       self->doProcessAndSave(data); //OK. 
     });
    }
    

    self = shared_from_this() 传递的是一个合法的 std::shared_ptr 实例,合法的类 Processor 对象的引用。


    深入 "std::enable_shared_from_this" 内部


    std::enable_shared_from_this 的实现类似:

    template<class T>
    class enable_shared_from_this {
     mutable weak_ptr weak_this;
    public:
     shared_ptr shared_from_this() {
      return shared_ptr(weak_this); 
     }
     //const overload
     shared_ptr<const T> shared_from_this() const {
      return shared_ptr<const T>(weak_this); 
     }
    
     //..more methods and constructors..
     //there is weak_from_this() also since C++17
    
     template <class U> friend class shared_ptr;
    };
    

    enable_shared_from_this 包含了一个 std::weak_ptr 指针,这正是函数 shared_from_this 返回的内容。注意,为什么不是 std::shared_ptr? 因为对象包含自身的计数引用会导致对象永远不被释放,从而发生内存泄漏。上述代码中 weak_this 会在类对象被 std::shared_ptr 引用时赋值,也就是std::shared_ptr 实例的构造函数中赋值,这也是为什么类 enable_shared_from_this 的最后,其被声明成为了 shared_ptr 的友元。


    总结


    到此,关于 std::enable_shared_from_this 的介绍就结束了。


    引用


    https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
    https://www.nextptr.com/tutorial/ta1414193955/enable_shared_from_this-overview-examples-and-internals

  • 相关阅读:
    【论文精读】Learning Transferable Visual Models From Natural Language Supervision
    【Linux】Linux项目自动化构建工具——make/Makefile
    狂神redis笔记10
    面试完我感觉五味杂陈,八年 Android 开发这种水平要20k?
    创新医疗器械行业机会渐行渐近
    英语音标中难发的音汇总
    【C++】C/C++内存管理
    计算机毕设(附源码)JAVA-SSM家用电器电商网站设计
    【Hack The Box】linux练习-- Tabby
    c++-基本计算器的灵活可扩展实现
  • 原文地址:https://www.cnblogs.com/Steven-HU/p/18252632