要点回顾


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

引言


本文介绍 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<T> 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<T> named pt, then calling T::shared_from_this will return a new std::shared_ptr<T> that shares ownership of t with pt.

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

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

使用 std::enable_shared_from_this<T>


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

假定有一个类 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> 实例,Processor 从 Client 类接收数据:

class Client {
public:
void someMethod() {
//...
processor->processData("Some Data");
//..do something else
}
private:
std::shared_ptr<Processor> 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>,这可能导致 Processor 的实例被析构。因此,在执行 execute 函数时或者在执行之前,上述代码中捕获的 this 指针随时可能会变为无效指针。

怎么办?

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

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

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

std::shared_ptr<T> 允许我们安全地访问和管理对象的生命周期。多个 std::shared_ptr<T> 实例通过一个共享控制块结构(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<T> 会创建一个新的控制块。从一个裸指针创建多个 std::shared_ptr<T> 实例会造成严重的后果:

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

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

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

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

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

深入 std::enable_shared_from_this<T> 内部


std::enable_shared_from_this<T> 的实现类似:

template<class T>
class enable_shared_from_this {
mutable weak_ptr<T> weak_this;
public:
shared_ptr<T> shared_from_this() {
return shared_ptr<T>(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<T> 指针,这正是函数 shared_from_this 返回的内容。注意,为什么不是 std::shared_ptr<T>? 因为对象包含自身的计数引用会导致对象永远不被释放,从而发生内存泄漏。上述代码中 weak_this 会在类对象被 std::shared_ptr<T> 引用时赋值,也就是std::shared_ptr<T> 实例的构造函数中赋值,这也是为什么类 enable_shared_from_this 的最后,其被声明成为了 shared_ptr 的友元。

总结


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

引用


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

何时/如何使用 std::enable_shared_from_this<T>?的更多相关文章

  1. 为什么使用enable_shared_from_this——shared_ptr两类错误

    在使用C++实现弱回调时,订阅者应当维护一系列发布者的weak_ptr,而发布者注册回调时要传出this的shared_ptr指针,流行的实现方法是使用std::enable_shared_from_ ...

  2. enable_shared_from_this类的作用和实现

    使用举例 实际中, 经常需要在一个被shared_ptr管理的对象的内部获取自己的shared_ptr. 比如: 通过this指针来构造一个shared_ptr, 如下: struct Bad { v ...

  3. 如何用enable_shared_from_this 来得到指向自身的shared_ptr 及对enable_shared_from_this 的理解

    在看<Linux多线程服务端编程:使用muduo C++网络库> 的时候,在说到如何防止在将对象的 this 指针作为返回值返回给了调用者时可能会造成的 core dump.需使用 ena ...

  4. C++11新特性之十:enable_shared_from_this

    enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为: template< class T > class enable_shar ...

  5. std::weak_ptr

    weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对 ...

  6. enable_shared_from_this

    头文件<memory> enable_shared_from_this是一个模板类. 使用场景:需要把自己类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr. str ...

  7. enable_shared_from_this用法分析

    一.背景 在为什么需要异步编程文章末尾提到,"为了使socket和缓冲区(read或write)在整个异步操作的生命周期一直保持活动,我们需要采取特殊的保护措施.你的连接类需要继承自enab ...

  8. C++11新特性:enable_shared_from_this

    enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为:template< class T > class enable_share ...

  9. C++11 新特性:enable_shared_from_this

    enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为:template< class T > class enable_share ...

  10. C++11笔记<一>

    目录: 1.std::share_ptr智能指针: 2.std::tr1::function模板类: 3.stringstream: 4.set/vector/map: 5.static_cast&l ...

随机推荐

  1. 使用友盟+的APM服务实现对移动端APP的性能监控

    ​简介: 对于信息系统服务,一般我们的重点监控对象都是核心的后端服务,通常会采用一些主流的APM(Application Performance Management)框架进行监控.告警.分析.那么对 ...

  2. [ML] 机器学习简介

    监督学习(Supervised Learning) 添加标签,手把手训练. 比如线性回归算法. 半监督学习(Semi-supervised Learning) 非监督学习(Unsupervised L ...

  3. [FAQ] docker-ce depends on containerd.io, docker-ce depends on docker-ce-cli

    安装 docker 缺少依赖会提示你安装,一般是以下两个: Package containerd.io is not installed Package docker-ce-cli is not in ...

  4. Threading Programming Guide:One

    苹果支持的产生线程的方式 Operation Object 使用OperationQueue,具体可以参考:Concurrency Programming Guide GCD 使用诸如dispatch ...

  5. iframe 高度设置为0时还有占位_iframe占位

    iframe是一个内联元素,默认是跟baseline对齐的,iframe后边有个看不见.摸不着的行内空白节点,空白节点占据着高度,iframe与空白节点的基线对齐,导致了div被撑开,从而出现滚动条, ...

  6. 关于浏览器sessionStorage共享问题

    今天也是闲暇之余看了点前端基础知识发现了好玩的 window.open("同源页面")这种方式新开的页面会复制之前的sessionStorage 通过a标签新开的页面同样也会,原理 ...

  7. Splashtop Enterprise提供全面的远程访问和远程支持解决方案

    ​ 全球领先的远程访问和远程支持解决方案领导者 Splashtop Inc. 发布了全新的 Splashtop Enterprise ,这是一个全面的远程访问和远程支持解决方案,满足企业的IT人员,服 ...

  8. C语言:将文件中所得到的单词表保存到一个顺序表中--使用动态分配数组。

    在很多时候我们想要在程序中存储想要的信息,但是又不知道该信息的大小或者说不知道需要多长的数组来存放.动态分配空间这个很好的解决了这个问题,动态分配不仅只可以用在链表中分配节点空间,其实更多时候用来分配 ...

  9. Clip-跳过

    在 Stable Diffusion 1.x 模型中,CLIP 用作文本嵌入.CLIP模型由多层组成.他们一层一层地变得更加具体.过于简单化,第一层可以理解"人",第二层可以区分& ...

  10. mews/captcha 验证码组件

    /** * 图像验证码 */ public function captcha(CaptchaBridge $captcha, $type = 'default') { $api_captcha = $ ...