std::enable_shared_from_this 有什么意义?

问:

这是boost里面举的一个例子:

class Y: public enable_shared_from_this<Y>
{
public:shared_ptr<Y> f(){return shared_from_this();}
}int main()
{shared_ptr<Y> p(new Y);shared_ptr<Y> q = p->f();assert(p == q);assert(!(p < q || q < p)); // p and q must share ownership
}

我的问题是:既然要专门定义一个类似f()的函数来获取另一个shared_ptr,为啥不直接像下面这么写呢?这不更符合我们使用 shared_ptr 的习惯吗?

int main()
{shared_ptr<X> p(new X);shared_ptr<X> q = p;assert(p == q);assert(!(p < q || q < p)); // p and q must share ownership
}

 

我来说个简单版本:就是为了不断了 shared_ptr 的传递链。

shared_ptr 是 “共享”型智能指针,但每次共享,都必须是复制已有的 shared_ptr ,比如:

 例1 (正确用法) ///// 创建:
std::shared_ptr<int> sp_0 { new int(999) }; 
// 开始共享:
std::shared_ptr<int> sp_1 = sp_0; // 复制源 shared_ptr 以实现共享

现在,sp_1 和 sp_0 就 互相“shared”了。而sp_1 也如前所述,是基于已有的 shared_ptr,也就是 sp_0 实现共享的。你可以把 " sp_1 = sp_0 " 这样的代码,理解为是两个智能指针借此,实现了互相通气,从而建立了“互相知情”的共享。

如果中间断开,硬是从原始的裸指针——虽然肯定也是同一个指针,但却不叫“共享”了:

 例2 (错误用法) ///// 创建:
std::shared_ptr<int> sp_0 { new int(999) }; 
// 开始“共享”:
std::shared_ptr<int> sp_1 { sp_0.get() }; // sp_1 基于祼指针创建

例2代码也能顺利编译,但运行时,有可能得到类似如下的输出:

free(): double free detected in tcache 2

原因就是 sp_1 和 sp_0 并没有实现共享,而是各自以为自己管理着某块内存,但它们其实管理的是同一块内存。可以把这种关系,理解为“互不知情”的共享——就是骗婚的那种,一个骗婚的女人,可能可以同时有6个老公,这6个老公本质是在共享,但完蛋就完蛋在“互不知情”。

为什么互不知情?因为 新的智能指针是基于裸指针建立的:

std::shared_ptr<int> sp_1 { sp_0.get() };// 等同于:int* tmp = sp_0.get();
std::shared_ptr<int> sp_1 {tmp}; // tmp 是裸指针

这就叫 shared_ptr 在传播的过程中掉链子了:某个新的 shared_ptr 竟然基于 裸 指针创建。

C++ class 中的 this,也是一个指针——更准确地讲是: this 也是一个“裸”指针。

那如果某个类的某个方法,要从 this 指针 创建一个新的 shared_ptr 怎么办?

this 是裸指针,所以如果基于它来创建一个新的shared_ptr,传播链必断啊!

比如:

struct A 
{std::shared_ptr<A> NewSharedPtr(){return std::shared_ptr<A>(this); // 基于 this 创建一个 share_ptr}
};void demo()
{// 创建shared_ptr<A> sp_0 {new A}; // 试图传播……auto sp_1 = sp_0->NewSharedPtr(); // 掉链子了
}

sp_1 和 sp_0,现在又是“互不知情”的共享了。掉链就发生在 NewSharedPtr() 这个方法里面:使用裸指针(在本例中,也就是 A* )创建了一个新的 shared_ptr<A> ,它将和之前任意一个 shared_ptr<A> 都互不知情,它以为自己在创建时,是独立拥有 this。

现在再来读 enable - shared - from - this 这个类名,就能明白不少吧:允许-共享-从-this。它就是一个工具(以基类的形式呈现),用于让我们不打断“shared”链,安全地创建出一个当前持有 this 对象的所有 shared_ptr 互相知情地共享的新 shared_ptr 。

更具体地说,enable_shared_from_this 基类将带来一个方法,叫 “shared_from_this()”,它的返回值就我们想要的。

结合上面例子,解决问题的方法,就是 为 struct A 加个基类 enable_shared_from_this :

struct A : public std::enable_shared_from_this<A>
{std::shared_ptr<A> NewSharedPtr(){return shared_from_this(); // 这回安全了}
};// demo 毫无变化 :
void demo()
{// 创建shared_ptr<A> sp_0 {new A}; // 试图传播……auto sp_1 = sp_0->NewSharedPtr(); // 成功!
}

当然啦,调用本例NewSharedPtr() ,从而最终调用:

enable_shared_from_this<A>::shared_from_this();

也得有注意事项:你当然得通过一个现有的shared_ptr<A> 来调用该方法,也就是最开始时的那一步“创建”第一个智能指针的步骤不可忽略,否则如何无中生有地生出第一个智能指针对象呢?

但是,以上都是你已经懂的,因为你问的例子,套到A身上来,就是:

void demo()
{// 创建shared_ptr<A> sp_0 {new A}; // 试图传播……auto sp_1 = sp_0; // 这样写,不比 sp_0->NewSharedPtr() 直观,简捷100倍吗?
}

对啊,为什么不直接写 sp_1 = sp_0 ,而非要写 sp_1 = sp_0->NewSharedPtr() 啊???

两个常见原因。

第一个原因是,有时候我们每当对外“分享”一次,就会额外在指针身上做一些事情,比如,记录一下 分享时的美好的心情:

struct A : public std::enable_shared_from_this<A>
{std::shared_ptr<A> NewSharedPtr(){std::cout << "分享是人类进步的台阶……\n" ;auto sp = shared_from_this(); // 这回安全了this->doSomethingAfterShared(); //                     return sp;}private:void doSomethingAfterShared(); 
};

第二原因,也是主要原因:有时候必须在类的方法往别的方法或函数传递一个新的分享,特别是异步操作时的回调。

假设有个 网络连接类:

// 有业务的伪代码:
class Connection : public std::enable_shared_from_this<A>
{public:void OnAccept(){std::shared_ptr<Connection> self { shared_from_this();};// 开始异步读,读到数据后,会调用 OnRead_socket.ReadSomeByte(buf, self, OnRead);     }void OnRead() {};
};

和A的例子区别在于: OnAccept() 方法中,传播的 self 并不用于 return,而是用于传递给别的方法,包括外部自由函数。这时候,这个传递就只能在 类的方法里执行,而在类的方法里,我们就真的只有 this 了,而没有 最初的那个源 shared_ptr,比如前面几个例子中的 sp_0.

具体到网络服务端异步处理,还有一个非常明确的需求:通过不断传递 shared_ptr,来确保这个 Connection 裸 指针一直存活,直到不做传递了,才(借助 shared_ptr 的工作原理),真正释放连接对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/5541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快手客户端一二面+美团前端一面+腾讯企业微信开发客户端一面

快手一面结志 1、自我介绍 2、对称加密非对称加密 3、TCP/UDP 4、在学校有什么课程是强项&#xff0c;说了过去几次面试中面到的C的语言基础知识 5、问C、Java中兴趣在哪里 6、问到项目&#xff0c;自己做的还是跟着学校老师做的&#xff0c;同样问到兴趣在哪里 7、LRU …

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

02 spring-boot+mybatis+elementui 的登录,文件上传,增删改查的入门级项目

前言 主要是来自于 朋友的需求 项目概况 就是一个 学生信息的增删改查 然后 具体到业务这边 使用 mybatis xml 来配置的增删改查 后端这边 springboot mybatis mysql fastjson hutool 的一个基础的增删改查的学习项目, 简单容易上手 前端这边 node14 vue element…

贪吃蛇小游戏(c语言)

1.效果展示 屏幕录制 2024-04-28 205129 2.基本功能 • 贪吃蛇地图绘制 • 蛇吃食物的功能 &#xff08;上、下、左、右方键控制蛇的动作&#xff09; • 蛇撞墙死亡 • 蛇撞自身死亡 • 计算得分 • 蛇身加速、减速 • 暂停游戏 3.技术要点 C语言函数、枚举、结构…

如何更好的使用cpm

nvidia发布了RAFT库&#xff0c;支持向量数据库的底层计算优化&#xff0c;RAFT 也使用CMake Package Manager( CPM )和rapids-cmake管理项目&#xff0c;可以方便快捷的下载到需要的对应版本的thirdparty的依赖库&#xff0c;但是&#xff0c;一般情况下&#xff0c;项目是直接…

C++多态(全)

多态 概念 调用函数的多种形态&#xff0c; 多态构成条件 1&#xff09;父子类完成虚函数的重写&#xff08;三同&#xff1a;函数名&#xff0c;参数&#xff0c;返回值相同&#xff09; 2&#xff09;父类的指针或者引用调用虚函数 虚函数 被virtual修饰的类成员函数 …

DSP开发实战教程-国产DSP替代进口TI DSP的使用技巧

1.替换CCS安装路径下的Flash.out文件 找到各自CCS的安装路径&#xff1a; D:\ti\ccs1230\ccs\ccs_base\c2000\flashAlgorithms 复制进芯电子国产DSP官网提供的配置文件 下载链接&#xff1a;https://mp.csdn.net/mp_download/manage/download/UpDetailed 2.替换原有文件 3.…

Python 深度学习(一)

原文&#xff1a;zh.annas-archive.org/md5/98cfb0b9095f1cf64732abfaa40d7b3a 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 序言 随着全球对人工智能的兴趣不断增长&#xff0c;深度学习引起了广泛的关注。每天&#xff0c;深度学习算法被广泛应用于不同行业。本书…

[GXYCTF 2019]BabyUpload

过滤 <? 且后缀不能有 php 上传1.jpg文件&#xff0c;内容为&#xff1a; <script languagephp>eval($_POST[cmd]);</script> 但文件后缀为.jpg&#xff0c;蚁剑不能连接。那怎么办呢&#xff1f; .htaccess文件&#xff1a;解析.jpg文件中的php代码 &#xf…

oracle的sqlplus默认会执行的脚本

我原来是知道sqlplus会默认执行$ORACLE_HOME/sqlplus/admin/glogin.sql这个脚本 今天在一个陌生的环境调用sqlplus时总会默认执行两条语句 但是就是找不到被执行的文件在哪里 后来发现是在环境变量 ORACLE_PATH下的login.sql文件 ORACLE_PATH这个环境变量是sqlplus这个工具使用…

【QEMU系统分析之实例篇(七)】

系列文章目录 第七章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第七章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么&#xff1f;二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.目标机器创建过程3.cpu_exec_init_all()io_mem_init()memory_…

HTML_CSS学习:CSS选择器

一、通配选择器 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>通配选择器</title><style>* {color: #1b8335;font-size: 40px;}/*可以选中所有的HTML元素*/<…

【C语言】——数据在内存中的存储

【C语言】——数据在内存中的存储 一、整数在内存中的存储1.1、整数的存储方式1.2、大小端字节序&#xff08;1&#xff09;大小端字节序的定义&#xff08;2&#xff09;判断大小端 1.3、整型练习 二、浮点数在内存中的存储2.1、引言2.2、浮点数的存储规则2.3、浮点数的存储过…

macbookproM2通过docker安装CDH

背景&#xff1a;项目中用的CDH大数据集群&#xff0c;但是自己的电脑上是MacM芯片的系统&#xff0c;网上在arm架构上搭建CDH集群的资料太少了&#xff0c;所以自己尝试搭建并且梳理一下文档 一、启动docker 我安装的是桌面版的docker 二、搜索CDH的镜像&#xff0c;然后拉…

【八大排序(三)】快速排序

❣博主主页: 33的博客❣ ▶️文章专栏分类:八大排序◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多排序知识 目录 1.前言2.快速排序2.1概念2.2画图理解2.3递归代码实现2.3.1Hoare法2.3.2挖坑法2.3.3前…

【介绍下OneFlow概念清单】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

智能健康管理系统的一次新体验

智能健康管理系统是一个集成了多方面数据资源&#xff0c;并配合人工智能算法的健康管理系统。该系统的应用涉及多个领域&#xff0c;包括医学、科学、生态和医疗保健等。其服务对象包括健康人群、亚健康人群和疾病人群&#xff0c;旨在通过病因预防、临床前期预防和临床预防三…

Java设计模式 _结构型模式_组合模式

一、组合模式 1、组合模式 组合模式&#xff08;Composite Pattern&#xff09;是这一种结构型设计模式。又叫部分整体模式。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次关系。即&#xff1a;创建了一个包含自己对象组的类&#xff0c;该类提供了修改…

代码随想录——双指针与滑动窗口(四)

一.1423. 可获得的最大点数 题目详情 解题思路 这里我们每次只能取最左或最右边的卡牌,第一反应其实是使用双指针&#xff0c;通过局部贪心来解决&#xff0c;但是如果两边相等的话用局部贪心无法来判断到底取哪一边&#xff0c;那我们不妨换一个思路&#xff1a; 我们首先任…

AI项目二十一:视频动态手势识别

若该文为原创文章&#xff0c;转载请注明原文出处。 一、简介 人工智能的发展日新月异&#xff0c;也深刻的影响到人机交互领域的发展。手势动作作为一种自然、快捷的交互方式&#xff0c;在智能驾驶、虚拟现实等领域有着广泛的应用。手势识别的任务是&#xff0c;当操作者做出…