C++关于shared_ptr和shared_from_this的说明

1.开发场景:

假设一个类中有一个handle()方法,该方法返回一个本类类型的share_ptr指针,外面有一个本类类型的shared_ptr指针去接该指针,该如何处理?

我们通常可能会认为在handle()方法中返回一个使用this指针初始化的shared_ptr,然后外面来接handle()方法的返回值,如下所示:

class Shared{
public: 
    Shared(int val):m_val(val){}
    int m_val;
    std::shared_ptr<Shared> handle()
    {
        //...
        return std::shared_ptr<Shared>(this);
    }
};

int main()
{
    std::shared_ptr<Shared> ptr1(new Shared(33));
    std::shared_ptr<Shared> ptr2 = ptr1->handle();
    std::cout<<ptr1.use_count()<<std::endl;
    std::cout<<ptr2.use_count()<<std::endl;
}

运行上述代码会直接挂掉,提示double free,原因是在初始化ptr1指针的时候,相当于用被实例化的对象的this指针给ptr1赋值了,在调用ptr1->handle()方法时,其内部的return std::shared_ptr<Shared>(this); 时又用this指针给一个临时的shared_ptr赋值,最终就相当于ptr2也引用自this指针,相当于同一个裸指针(this)给不同的shared_ptr赋值,最终产生double free的问题。

产生double free问题的根因就是,无法通过原指针增加shared_ptr智能指针的引用计数(无法将一个原指针给多个shared_ptr赋值,因为不会增加引用计数,导致引用的空间被提前释放掉)。从打印也可以看出,两个ptr1和ptr2的引用计数都为1。

 当然如果ptr1不是智能指针,是裸指针的话,是没问题的,如下:

int main()
{
    Shared *ptr1(new Shared(33));
    std::shared_ptr<Shared> ptr2 = ptr1->handle();
}

2.解决方案

c++标准库为了解决上述无法通过原指针增加shared_ptr引用计数的问题,提供了enable_shared_from_this<Typename>类模板。具体做法是将待操作的类声明为std::enable_shared_from_this<Typename>模板类的派生类,然后在handle()方法中return shared_from_this; 如下所示:

class Shared : public std::enable_shared_from_this<Shared>{
public: 
    Shared(int val):m_val(val){}
    int m_val;
    std::shared_ptr<Shared> handle()
    {
        //...
        return shared_from_this();
    }
};

int main()
{
    std::shared_ptr<Shared> ptr1(new Shared(33));
    std::shared_ptr<Shared> ptr2 = ptr1->handle();
    std::cout<<ptr1.use_count()<<std::endl;
    std::cout<<ptr2.use_count()<<std::endl;
}

输出结果如下:

 3.原理分析与引申

其实enable_shared_from_this<>模板类内部通过一个weak_ptr指针作为中间体,weak_ptr首先引用自shared_ptr,然后在需要返回shared_ptr的函数内部,返回一个用weak_ptr初始化的shared_ptr,这样就ok了。因为使用shared_ptr给weak_ptr初始化和赋值不会改变shared_ptr的引用计数。

enable_shared_from_this<>源码:

 其实我们也可以自己再类内部定义一个weak_ptr指针来测试源码中的功能,具体如下:

class Shared{
public: 
    Shared(int val):m_val(val){}
    int m_val;
    std::shared_ptr<Shared> handle()
    {
        //...
        return std::shared_ptr<Shared>(m_weak_ptr_this);
    }
    std::weak_ptr<Shared> m_weak_ptr_this;
};

int main()
{
    Shared *p = new Shared(33);
    //裸指针p先给ptr1赋值
    std::shared_ptr<Shared> ptr1(p);
    //使用shared_ptr给weak_ptr赋值
    ptr1->m_weak_ptr_this = ptr1;
    //再操作裸指针给ptr2赋值
    std::shared_ptr<Shared> ptr2 = p->handle();
    std::cout<<ptr1.use_count()<<std::endl;
    std::cout<<ptr2.use_count()<<std::endl;
}

输出结果:

关于shared_from_this()的详细说明可参考C++:智能指针(5)——enable_shared_from_this工作原理、源码分析_enable_shared_from_this原理分析_cocoa0409的博客-CSDN博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值