C 智能指针enable_shared_from_this
创始人
2025-07-06 18:40:39
0

enable_shared_from_this介绍

enable_shared_from_this其实是智能指针中的内容,它的作用就是用于在类的内部,返回一个this的智能指针。

对于enable_shared_from_this,初学者可能不明白它的使用场景和使用的必要性,可能有得童鞋们会问既然有了this这个指向自己的指针, 为什么还需要enable_shared_from_this这个东西呢,直接用this代替不就好了吗?

我们来看看以下代码例子,如果先不运行,你能看出什么问题吗?

#include 
class Person{
public:
    Person() = default;
    ~Person(){

    };
    std::shared_ptr getPtr(){
        return std::shared_ptr(this);
    }
};

int main() {
    std::shared_ptr person = std::make_shared();
    std::shared_ptr person1 = person->getPtr();
    std::cout << "person.use_count() = " << person.use_count() << std::endl;
    std::cout << "person1.use_count() = " << person1.use_count() << std::endl;
    return 0;
}

以上代码运行崩溃报错了,这是为什么呢?

崩溃信息

这是因为只有一个Person的指针,但是却被两个智能指针shared_ptr持有,而它们的引用计数都是1,因此当main函数运行完毕后两个智能指针释放时都对同一个Person指针进行释放导致的崩溃。

如果我们能让两个智能指针shared_ptr共享同一个引用计数,那么这个崩溃问题就迎刃而解了。而通过让Person继承基类enable_shared_from_this,然后在函数getPtr中 调用基类的shared_from_this就能返回一个this的智能指针,这样即可实现让多个智能指针共享同一个引用计数,而达到销毁时只释放一次的目的。这就是enable_shared_from_this存在的必要性, 这也是this无法替代的功能点。

如下是实例代码:

#include 
class Person:public std::enable_shared_from_this{
public:
    Person() = default;
    ~Person(){

    };
    std::shared_ptr getPtr(){
        return shared_from_this();
    }
};

int main() {
    std::shared_ptr person = std::make_shared();
    std::shared_ptr person1 = person->getPtr();
    std::cout << "person.use_count() = " << person.use_count() << std::endl;
    std::cout << "person1.use_count() = " << person1.use_count() << std::endl;
    return 0;
}

通过运行调试打印,我们可以看到这person和person1这两个智能指针的引用计数都变为了2,这是正确的。

通过两个实例代码的对比,我们可以发现问题的根源所在就是我们在返回this的智能指针时,直接调用std::shared_ptr构造函数传入裸指针的方式构造一个智能指针, 而在之前的介绍中我们提到过使用智能指针shared_ptr时尽量使用std::make_shared进行智能指针的构造,避免直接调用std::shared_ptr构造函数传入裸指针的方式进行构造。

更多关于enable_shared_from_this的实践对比可以参照官网学习:https://en.cppreference.com/w/cpp/memory/enable_shared_from_this

enable_shared_from_this的实现

我们通过源码的方式来分析下enable_shared_from_this的实现原理,enable_shared_from_this的源码非常简短:

template
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(__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 weak_from_this() const _NOEXCEPT
        { return __weak_this_; }
#endif // _LIBCPP_STD_VER > 14

    template  friend class shared_ptr;
};

通过源码我们可以发现这是一个模版类,将自身类型以模版参数的形式传入到父类,这是典型的CRTP应用,关于CRTP之前我们已经介绍过了,这里不再累赘。感兴趣的童鞋们可以参考之前的博文:

C++之CRTP的使用

enable_shared_from_this对外只提供了一个weak_from_this公共方法,其内部通过以为弱引用的智能指针weak_ptr构造了一个shared_ptr,这里并没有什么问题, 问题这个弱引用的智能指针__weak_this_它是在哪里初始化的呢?我们通shared_ptr的构造函数可以发现,如果传入的weak_ptr没有初始化的话是会抛出异常崩溃的。

其实成员变量__weak_this_的初始化是在类的外部进行初始化的,它的奥秘就是源码的倒数第二行template ();改为不使用智能指针, 而使用裸指针的方式,修改为 auto person = new Person;,同时注释掉第16行再运行是会崩溃的,这就是因为__weak_this_没有进行初始化的原因。

崩溃信息

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...