何时/如何使用 std::enable_shared_from_this<T>?
要点回顾
- 继承自
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_this
及 std::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 astd::shared_ptr
named ptto 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 functionshared_from_this
. If an object t of type T is managed by astd::shared_ptr<T>
named pt, then callingT::shared_from_this
will return a newstd::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>?的更多相关文章
- 为什么使用enable_shared_from_this——shared_ptr两类错误
在使用C++实现弱回调时,订阅者应当维护一系列发布者的weak_ptr,而发布者注册回调时要传出this的shared_ptr指针,流行的实现方法是使用std::enable_shared_from_ ...
- enable_shared_from_this类的作用和实现
使用举例 实际中, 经常需要在一个被shared_ptr管理的对象的内部获取自己的shared_ptr. 比如: 通过this指针来构造一个shared_ptr, 如下: struct Bad { v ...
- 如何用enable_shared_from_this 来得到指向自身的shared_ptr 及对enable_shared_from_this 的理解
在看<Linux多线程服务端编程:使用muduo C++网络库> 的时候,在说到如何防止在将对象的 this 指针作为返回值返回给了调用者时可能会造成的 core dump.需使用 ena ...
- C++11新特性之十:enable_shared_from_this
enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为: template< class T > class enable_shar ...
- std::weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对 ...
- enable_shared_from_this
头文件<memory> enable_shared_from_this是一个模板类. 使用场景:需要把自己类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr. str ...
- enable_shared_from_this用法分析
一.背景 在为什么需要异步编程文章末尾提到,"为了使socket和缓冲区(read或write)在整个异步操作的生命周期一直保持活动,我们需要采取特殊的保护措施.你的连接类需要继承自enab ...
- C++11新特性:enable_shared_from_this
enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为:template< class T > class enable_share ...
- C++11 新特性:enable_shared_from_this
enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为:template< class T > class enable_share ...
- C++11笔记<一>
目录: 1.std::share_ptr智能指针: 2.std::tr1::function模板类: 3.stringstream: 4.set/vector/map: 5.static_cast&l ...
随机推荐
- 打通JAVA与内核系列之一ReentrantLock锁的实现原理
简介:写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁).其中synchronized锁是JAVA语言层面提供的能力 ...
- 阿里云重磅发布业务中台产品 BizWorks,中台发展进入下一个阶段
简介: 业务中台产品BizWorks重磅发布,这可以看作是阿里云在 "做厚中台" 战略上继 "云钉一体"之后的又一个新动作! 10 月 19 日,2021 云 ...
- Git 版本控制:构建高效协作和开发流程的最佳实践
引言 版本控制是开发中不可或缺的一部分,他允许多人同时协作,通过记录每一次代码的变更,帮助开发者理解何时.为什么以及谁做了修改.这不仅有助于错误追踪和功能回溯,还使得团队能够并行工作,通过分支管理实现 ...
- 005_Orcad里创建Homogeneous分裂元件
005_Orcad里创建Homogeneous分裂元件 两种类型Homogeneous和Hetergeneous的区别,都是用来把一个复杂的元件分成多个部分来画,不同的是homogeneous画的每部 ...
- linux导出安装包
linux导出安装包 1 背景 部署企业内网环境,主机无法连通外网.不能直接使用yum install安装程序.针对此种情况有如下两个安装办法 源码安装(需要编译环境,安装复杂,容易出错,不推荐) 使 ...
- SAP Adobe Form 教程四 动态隐藏和显示字段
前文: SAP Adobe Form 教程一 简单示例 SAP Adobe Form 教程二 表 SAP Adobe Form 教程三 日期,时间,floating field 本文链接:https: ...
- Solution Set - 点分治
A[POJ1741].给定一棵树,边有权,求长度不超过\(k\)的路径数目. B[HDU4871].给定一张图,边有权,求它的最短路径树上恰含\(k\)个点的路径中最长路径的长度及数目. C[HDU4 ...
- 【python爬虫案例】用python爬取百度的搜索结果!2023.3发布
目录 一.爬取目标 二.展示结果数据 三.编写爬虫代码 3.1 请求头和cookie 3.2 分析请求地址 3.3 分析页面元素 3.4 获取真实地址 3.5 保存结果数据 四.同步讲解视频 五.附完 ...
- CCE云原生混部场景下的测试案例
本文分享自华为云社区<CCE云原生混部场景下在线任务抢占.压制离线任务CPU资源.保障在线任务服务质量效果测试>,作者:可以交个朋友. 背景 企业的 IT 环境通常运行两大类进程,一类是在 ...
- threejs