enable_shared_from_this使用介绍

2023-12-05

enable_shared_from_this定义

定义于头文件

template< class T > class enable_shared_from_this;  (C++11) 

std::enable_shared_from_this 能让其一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, … ) ,它们与 pt 共享对象 t 的所有权。

若一个类 T 继承 std::enable_shared_from_this ,则会为该类 T 提供成员函数: shared_from_this。当 T 类型对象 t 被一个为名为 pt 的 std::shared_ptr 类对象管理时,调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr 对象,它与 pt 共享 t 的所有权。

使用场合

  • 需要在类对象的内部中获得一个指向当前对象的 shared_ptr 对象。
  • 通过类的成员函数安全的获取对象的this指针,一般来说我们不建议直接返回this指针,可以想象下有这么一种情况,返回的this指针保存在外部一个局部/全局变量,当对象已经被析构了,但是外部变量并不知道指针指向的对象已经被析构了,如果此时外部使用了这个指针就会发生程序奔溃。

崩溃示例:

#include <iostream>
#include <memory>

class Foo{
public:
    Foo(){
        std::cout << "Foo::Foo constructor run" << std::endl;
    }
    ~Foo(){
        std::cout << "Foo::~Foo destructor run" << std::endl;
    }

    std::shared_ptr<Foo> GetSharedObject(){
        return std::shared_ptr<Foo>(this);
    }
};

int main()
{
    std::shared_ptr<Foo> p(new Foo());
    std::shared_ptr<Foo> q = p->GetSharedObject();

    std::cout << p.use_count() << std::endl;
    std::cout << q.use_count() << std::endl;

    return 0;
}

程序运行输出:

Foo::Foo constructor run
1
1
Foo::~Foo destructor run
Foo::~Foo destructor run

从程序运行输出看出,析构函数调用了2次,这明显跟我们的预期不符。

借助指针指针可以很轻松的解决这个问题:

#include <iostream>
#include <memory>

class Foo : public std::enable_shared_from_this<Foo>
{
public:
    Foo(){
        std::cout << "Foo::Foo constructor run" << std::endl;
    }
    ~Foo(){
        std::cout << "Foo::~Foo destructor run" << std::endl;
    }

	std::shared_ptr<Foo> GetSharedObject(){
        return shared_from_this();
    }
};

int main()
{
    std::shared_ptr<Foo> p(new Foo());
    std::shared_ptr<Foo> q = p->GetSharedObject();

    std::cout << p.use_count() << std::endl;
    std::cout << q.use_count() << std::endl;

    return 0;
}

程序运行输出:

Foo::Foo constructor run
2
2
Foo::~Foo destructor run

源码实现

template<typename _Tp>
class enable_shared_from_this
{
protected:
    enable_shared_from_this() { }
    
    enable_shared_from_this(const enable_shared_from_this&) { }

    enable_shared_from_this&
    operator=(const enable_shared_from_this&)
    { return *this; }

    ~enable_shared_from_this() { }

public:
    shared_ptr<_Tp>
    shared_from_this()
    { return shared_ptr<_Tp>(this->_M_weak_this); }

    shared_ptr<const _Tp>
    shared_from_this() const
    { return shared_ptr<const _Tp>(this->_M_weak_this); }

private:
    /**
	 *@brief _M_weak_assign函数在类型T被包覆在shared_ptr的过程中会被调用
	 */
    template<typename _Tp1>
    void
    _M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const
    { _M_weak_this._M_assign(__p, __n); }

    template<typename _Tp1>
    friend void
    __enable_shared_from_this_helper(const __shared_count<>& __pn,
                    const enable_shared_from_this* __pe,
                    const _Tp1* __px)
    {
    if (__pe != 0)
    __pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
}

    mutable weak_ptr<_Tp>  _M_weak_this; // const函数也可以修改该变量
};

/**
 *@brief 共享智能指针
 */
template<typename _Tp>
class shared_ptr
: public __shared_ptr<_Tp>
{
public:
  shared_ptr()
  : __shared_ptr<_Tp>() { }
	// ...
};

template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
public:
  typedef _Tp   element_type;
  
  __shared_ptr()
  : _M_ptr(0), _M_refcount() // never throws
  { }
  
  // 智能指针构造会调用__enable_shared_from_this_helper
  template<typename _Tp1>
	explicit
	__shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
	{
  __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
  typedef int _IsComplete[sizeof(_Tp1)];
  __enable_shared_from_this_helper(_M_refcount, __p, __p);
}
	//...
};

注意

enable_shared_from_this 其内部保存着一个对 this 的弱引用(例如 std::weak_ptr )。 std::shared_ptr 的构造函数检测无歧义且可访问的 (C++17 起) enable_shared_from_this 基类,并且若内部存储的弱引用未为生存的 std::shared_ptr 占有,则 (C++17 起)赋值新建的 std::shared_ptr 为内部存储的弱引用。为已为另一 std::shared_ptr 所管理的对象构造一个 std::shared_ptr ,将不会考虑内部存储的弱引用,从而将导致未定义行为。

只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this 。否则调用行为未定义 (C++17 前)抛出 std::bad_weak_ptr 异常(通过 shared_ptr 从默认构造的 weak_this 的构造函数) (C++17 起)。

enable_shared_from_this 提供安全的替用方案,以替代 std::shared_ptr(this) 这样的表达式(这种不安全的表达式可能会导致 this 被多个互不知晓的所有者析构)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

enable_shared_from_this使用介绍 的相关文章

随机推荐

  • Sequential Modeling Enables Scalable Learning for Large Vision Models

    目录 一 论文速读 1 1 摘要 1 2 论文概要总结 二 论文精度 2 1 论文试图解决什么问题 2 2 论文中提到的解决方案之关键是什么 2 3 论文提出的架构和损失函数是什么 2 4 用于定量评估的数据集是什么 代码有没有开源 2 5
  • AI大模型专题:北京市人工智能行业大模型创新应用白皮书

    今天分享的是人工智能系列深度研究报告 AI大模型专题 北京市人工智能行业大模型创新应用白皮书 报告出品方 北京市科学技术委员会 中关村科技园区管理委员会 报告共计 72页 海量 完整电子版 报告下载方式 公众号 人工智能学派 综述 作为新一
  • 学生犯错误老师应该怎么教育

    作为一名老师 当学生犯错误时 我们需要采取一些措施来帮助他们改正错误并学习如何更好地处理问题 以下是我作为一名老师会采取的几个步骤 进行私下谈话 了解他们为什么犯错误 我会听取他们的解释 并尝试理解他们的动机 这样做可以让我更好地了解学生的
  • 允许root远程连接数据库

    开放root远程连接数据库的权限 Linux系统 环境 centos7 关闭防火墙 没关要开放数据库的端口 一 进入数据库 查看权限表信息 MariaDB none gt use mysql Reading table informatio
  • 为什么最近听说 Go 岗位很少很难?

    大家好 我是煎鱼 其实这个话题已经躺在我的 TODO 里很久了 近来很多社区的小伙伴都私下来交流 也有在朋友圈看到朋友吐槽 Go 上海的大会没什么人 还不如 Rust 大会 比较尴尬 今天主要是看看为什么 Go 岗位看起来近来很难的样子 也
  • 计算机组成与设计:硬件/软件接口,第三章详细梳理,附思维导图

    文章目录 三 计算机的运算 章节导图 一 整数的表示 无符号整数 原码 反码 原码是带符号整数的表示方法
  • Liunx常用基础命令

    目录 liunx常用命令基础 1 cd命令 2 ls命令 3 pwd命令 4 touch命令 5 head命令 6 cat命令 7 more命令
  • 网络安全日报 2023年12月04日

    1 研究人员披露Lazarus组织已窃取价值30亿美元加密货币 https go recordedfuture com hubfs reports cta 2023 1130 pdf 至少自2017年以来 来自朝鲜的Lazarus组织越来越
  • Linux(13):例行性工作排程

    例行性工程 听谓的排程是将工作安排执行的流程之意 Linux 排程就是透过 crontab 与 at 这两个东西 两种工作排程的方式 一种是例行性的 就是每隔一定的周期要来办的事项 一种是突发性的 就是这次做完以后就没有的那一种 at at
  • 软件测试/人工智能|Python 变量解析:从基础概念到内存地址探究

    变量 什么是变量 变量是在程序中用于存储数据的名称 它们可以存储各种类型的数据 比如数字 文本 列表 字典等等 变量类型 在介绍变量时 可以提及 Python 中常见的变量类型 例如整数 浮点数 字符串 布尔值 列表 元组 字典等 如下所示
  • 分享一个字节面试题:如何实现准时的setTimeout

    最近有同学在面试的时候被问到了这个问题 所以我们利用这篇文章对这个问题进行下解答 背景 setTimeout 是 不准 的 因为 setTimeout 是一个宏任务 它的指定时间指的是 进入主线程的时间 setTimeout callbac
  • span标签点击去掉光标

    很简单 一行样式搞定 caret color transparent
  • 对象转成json后转成byte[]后在转成string会提示序列化失败,第一个字符是问号

    问题复现 一个对象需要转成json 后转成byte 后经过网络传输 后再次反序列化为对象 但是最后反序列的时候会报错 打印json发现开头是一个问号 省流 使用这个进行反序列化
  • 【JavaScript】2.1 高级语法特性

    在JavaScript的基础部分 我们已经学习了变量 数据类型 操作符 流程控制 函数 事件和DOM操作等基础知识 接下来 我们将学习一些JavaScript的高级语法特性 包括闭包 原型和原型链 作用域和作用域链 异步编程和Promise
  • 网站防盗链是什么

    随着互联网的快速发展 网站的安全问题越来越受到关注 其中 防盗链是许多网站面临的一个重要问题 本文将介绍网站防盗链的基本概念 原因以及如何采取措施进行保护 一 什么是网站防盗链 网站防盗链是指未经授权的网站通过技术手段获取并使用其他网站的资
  • 微信扫码登录修改二维码的样式

    默认是这个样子二维码都没有展示全 微信的了的 js 对象是这个样子 既然大家看到我这篇文章 想必里面的属性已经知道了 这里不做赘述 let href data text css base64 LmltcG93ZXJCb3ggLnFyY29k
  • python+requests接口自动化测试框架实例详解教程

    前段时间由于公司测试方向的转型 由原来的web页面功能测试转变成接口测试 之前大多都是手工进行 利用postman和jmeter进行的接口测试 后来 组内有人讲原先web自动化的测试框架移驾成接口的自动化框架 使用的是java语言 但对于一
  • MN316 OpenCPU丨Flash使用介绍

    在MN316 标准版SDK中 定义了操作模组内置flash接口 用户可操作空间为64KB 分为16个block 每个block大小为4KB 用户如有操作flash的需求 可调用相关接口 FOTA使用流程解析 以下流程图为使用 MN316 O
  • 聊聊刻意练习-构建心理表征

    这是鼎叔的第八十一篇原创文章 行业大牛和刚毕业的小白 都可以进来聊聊 欢迎关注本专栏和微信公众号 敏捷测试转型 星标收藏 大量原创思考文章陆续推出 本人新书 无测试组织 测试团队的敏捷转型 已出版 机械工业出版社 各大电商平台热销中 30万
  • enable_shared_from_this使用介绍

    文章目录 enable shared from this定义 使用场合 源码实现 注意 enable shared from this定义 定义于头文件 template lt class T gt class enable shared