首页 > 编程语言 >C++里std::enable_shared_from_this是干什么用的?

C++里std::enable_shared_from_this是干什么用的?

时间:2023-08-18 11:48:36浏览次数:36  
标签:std enable weak shared ptr 指针

std::enable_shared_from_this使用场景

在很多场合,经常会遇到一种情况,如何安全的获取对象的this指针,一般来说我们不建议直接返回this指针,可以想象下有这么一种情况,返回的this指针保存在外部一个局部/全局变量,当对象已经被析构了,但是外部变量并不知道指针指向的对象已经被析构了,如果此时外部使用了这个指针就会发生程序奔溃。既要像指针操作对象一样,又能安全的析构对象,很自然就想到,智能指针就很合适!

那么智能指针如何使用呢?现在我们来看一段代码。

#include <iostream>
#include <memory>

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

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

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

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

    return 0;
}

编译运行后程序输出如下:

Widget constructor run
1
1
Widget destructor run
Widget destructor run
22:06:45: 程序异常结束。

从输出我们可以看到,调用了一次构造函数,却调用了两次析构函数,很明显这是不正确的。而std::enable_shared_from_this正是为了解决这个问题而存在。

std::enable_shared_from_this原理和实战

前面我们说使用std::enable_shared_from_this能解决安全获取this指针的问题。在使用之前,我们先来了解下std::enable_shared_from_this是什么?为什么能解决这个问题?std::enable_shared_from_this定义如下:

template<class _Tp>
class _LIBCPP_TEMPLATE_VIS enable_shared_from_this
{
    mutable weak_ptr<_Tp> __weak_this_;
protected:
    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
    enable_shared_from_this() _NOEXCEPT {}
    _LIBCPP_INLINE_VISIBILITY
    enable_shared_from_this(enable_shared_from_this const&) _NOEXCEPT {}
    _LIBCPP_INLINE_VISIBILITY
    enable_shared_from_this& operator=(enable_shared_from_this const&) _NOEXCEPT
        {return *this;}
    _LIBCPP_INLINE_VISIBILITY
    ~enable_shared_from_this() {}
public:
    _LIBCPP_INLINE_VISIBILITY
    shared_ptr<_Tp> shared_from_this()
        {return shared_ptr<_Tp>(__weak_this_);}
    _LIBCPP_INLINE_VISIBILITY
    shared_ptr<_Tp const> shared_from_this() const
        {return shared_ptr<const _Tp>(__weak_this_);}

#if _LIBCPP_STD_VER > 14
    _LIBCPP_INLINE_VISIBILITY
    weak_ptr<_Tp> weak_from_this() _NOEXCEPT
       { return __weak_this_; }

    _LIBCPP_INLINE_VISIBILITY
    weak_ptr<const _Tp> weak_from_this() const _NOEXCEPT
        { return __weak_this_; }
#endif // _LIBCPP_STD_VER > 14

    template <class _Up> friend class shared_ptr;
};

std::enable_shared_from_this是模板类,内部有个_Tp类型weak_ptr指针,调用shared_from_this成员函数便可获取到_Tp类型智能指针,从这里可以看出,_Tp类型就是我们的目标类型。再来看看std::enable_shared_from_this的构造函数都是protected,因此不能直接创建std::enable_from_shared_from_this类的实例变量,只能作为基类使用。因此使用方法如下代码所示:

#include <iostream>
#include <memory>

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

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

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

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

    return 0;
}

这里为什么要创建智能指针p而不是直接创建裸指针p?根本原因在于std::enable_shared_from_this内部的weak_ptr,若只是创建裸指针p,那么p被delete后仍然面对不安全使用内部this指针问题。因此p只能被定义为智能指针。当p被定义为shared_ptr智能指针后,p指针引用计数是1(weak_ptr不会增加引用计数),再通过shared_from_this获取内部this指针的智能指针,则p的引用计数变为2。

现编译运行输出如下:

Widget constructor run
2
2
Widget destructor run

正确的返回了智能指针,p和q的引用计数都是2,且只调用了一次构造函数和析构函数,不会错误的析构对象多次。

标签:std,enable,weak,shared,ptr,指针
From: https://www.cnblogs.com/blizzard8204/p/17639998.html

相关文章

  • C++ 多线程详解之异步编程 std::packaged_task
    std::packaged_task将任何可调用对象(比如函数、lambda表达式等等)封装成一个task,可以异步执行。执行结果可以使用std::future获取。比如下面的例子,构造一个std::packaged_task后,get_future()函数返回一个std::future对象,可以获取task异步或者同步执行的结果。#includ......
  • #yyds干货盘点#FastDFS配置Nginx访问
    下载相关依赖软件包yum-yinstallwgetmakezlibzlib-develgcc-c++libtoolopensslopenssl-develwgethttp://nginx.org/download/nginx-1.10.2.tar.gztar-xzvfnginx-1.10.2.tar.gz安装Nginxcdnginx-1.10.2./configure--prefix=/data/apps/nginx-download\--p......
  • 关于callback和std::bind的那些事
    前言使用callback常常需要绑定类的具体函数,哪些可以绑定哪些不能?分析callback不同与普通函数,其入参也是一个函数,具体行为由入参决定我们看这样一段代码https://godbolt.org/z/4YTKs567j#include<functional>#include<iostream>classA{public:voidprint(constint&......
  • c++ std::to_string实现原理
    写这篇的起因是看到MSVCSTL的一个issue,里面提到to_string<int>的实现,正常人的思维是直接除10拿到每位,其实有个更高效的查表法字符串转数字除100拿到两位,并查表填入,少了一半的除法,代价是需要一个201个byte的空间,下面是gcc的实现//Writeanunsignedintegervaluetother......
  • SharedPreferences
    SharedPreferences简介在Android开发过程中,有时候我们需要保存一些简单的软件配置等简单数据的信息,而如果我们直接用数据库存储的话又不太方便,在这里我们就可以用到SharedPreferences,SharedPreferences保存的数据主要是类似于配置信息格式的数据,因此保存的数据主要是简单类型的......
  • C++ 字符串拼接技巧(stringstream、字符串迭代器、字符串的加法运算符、std::accumulat
    在C++中,经常需要将多个字符串拼接成一个大字符串。这个过程很容易出错,但有一些技巧可以帮助我们轻松地实现这个目标。本文将介绍一些C++中join字符串的技巧。一、使用stringstreamstringstream是一个流。使用它可以将多个字符串连接起来,然后将它们转换为一个字符串。可......
  • std::optional的使用
    optional是一个模板类:template<classT>classoptional;它内部有两种状态,要么有值(T类型),要么没有值(std::nullopt)。有点像T*指针,要么指向一个T类型,要么是空指针(nullptr)。std::optional有以下几种构造方式:#include<iostream>#include<optional>usingnamespacestd;intmai......
  • ISTDP:心灵的深海潜水
    (仅供参考)有一天,我被一种名为ISTDP(IntensiveShort-TermDynamicPsychotherapy,强化短期动态心理治疗)的心理治疗方法所吸引。我决定深入研究这个看似神秘的名字背后的内容。就像一个深海潜水员,我将踏上一个探索我们情感深处、挖掘我们心灵深层防御机制的旅程。1.ISTDP:强化短期动......
  • 函数指针、std::function、std::bind
    函数指针、std::function、std::bind函数指针:C++语法中可以直接将函数名作为指针,voidfun(inta,intb);在这个函数声明中,函数指针即为fun,传入要被调用的地方时只需要传入fun就可以。但是这个函数指针是什么类型呢?需要明确函数指针类型及其写法,我们才能在要调用函数的形......
  • ORA-01034: ORACLE not available、ORA-27101: shared memory realm does not exist
    发生缘由学习Oracle的使用,结果关机之后重新使用SQLPlus发现无法登录--windowsserver2003使用sqlplus连接oracle报错C:\DocumentsandSettings\Adminstrator>sqlplussystem/linxuanORA-01034:ORACLEnotavailableORA-27101:sharedmemoryrealmdoesnotexist......