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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
shared_ptrC++中用于动态内存管理的智能指针之一。它能够记录对象被引用的次数,主要用于管理动态创建的对象的销毁。使用shared_ptr可以避免内存泄漏和悬挂指针等问题。 在C++中,实现shared_ptr通常需要以下几个步骤: 1. 定义一个模板类,例如SHARED_ptr,该类将作为智能指针使用。 2. 在SHARED_ptr类中,定义一个指针成员变量ptr,用于指向实际的对象。 3. 在SHARED_ptr类中,定义一个计数器类的指针成员变量refcount,用于记录对象被引用的次数。 4. 在SHARED_ptr类中,实现构造函数,用于初始化指针和计数器。 5. 在SHARED_ptr类中,实现拷贝构造函数和赋值操作符重载,用于处理多个智能指针共享同一对象的情况。 6. 在SHARED_ptr类中,实现析构函数,用于释放对象的内存空间。 7. 在SHARED_ptr类中,实现箭头运算符重载和解引用运算符重载,用于访问对象的成员函数和数据。 8. 在SHARED_ptr类外部,实现计数器类RefCount,用于记录对象被引用的次数,并在引用次数为0时释放对象的内存空间。 实现shared_ptr的详细代码如下所示: ```cpp template <class T> class SHARED_ptr { private: T* ptr; // 用来指向堆区对象 RefCount<T>* refcount; // 指向计数器对象的指针 public: SHARED_ptr(T* p) : ptr(p), refcount(new RefCount<T>()) { refcount->increment(); } SHARED_ptr(const SHARED_ptr<T>& other) : ptr(other.ptr), refcount(other.refcount) { refcount->increment(); } ~SHARED_ptr() { if (refcount->decrement() == 0) { delete ptr; delete refcount; } } T* operator->() const { return ptr; } T& operator*() const { return *ptr; } SHARED_ptr& operator=(const SHARED_ptr<T>& other) { if (this != &other) { if (refcount->decrement() == 0) { delete ptr; delete refcount; } ptr = other.ptr; refcount = other.refcount; refcount->increment(); } return *this; } }; ``` 以上是一个简单的C++实现shared_ptr的示例代码。通过使用shared_ptr,我们可以方便地管理动态创建的对象的生命周期,并避免内存泄漏和悬挂指针等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值