c++智能指针的enable_shared_from_this和shared_from_this机制

c++智能指针的enable_shared_from_this和shared_from_this机制

问题:

  1. 用shared_ptr智能指针管理指针p所指向的对象时,如果用的是如下的形式,那么ptr1.use_count和ptr2.use_count都会是1。那么同一个new A会被析构两次。
	A *p = new A(); // 裸指针指向堆上的对象
	shared_ptr<A> ptr1(p);// 用shared_ptr智能指针管理指针p指向的对象
	shared_ptr<A> ptr2(p);// 用shared_ptr智能指针管理指针p指向的对象
  1. 又或者是在A类中提供一个成员方法来返回指向自身的shared_ptr,还是出现析构两次的情况。
shared_ptr<A> getSharedPtr() 
	{ 
		return shared_ptr<A>(this); 
	}
	shared_ptr<A> ptr1(new A());
	shared_ptr<A> ptr2 = ptr1->getSharedPtr();

原因:

shared_ptr继承自_Ptr_base,成员变量中有一个指向资源的指针,有一个指向资源引用计数的指针,智能指针本身是8个字节。

// _Ptr_base的两个成员变量,这里只罗列了_Ptr_base的部分代码
	element_type * _Ptr{nullptr}; // 指向资源的指针
	_Ref_count_base * _Rep{nullptr}; // 指向资源引用计数的指针

_Ref_count_base记录资源的类定义:

class __declspec(novtable) _Ref_count_base
	{	// common code for reference counting
private:
	/* _Uses记录了资源的引用计数,也就是引用资源的shared_ptr
	的个数;_Weaks记录了weak_ptr的个数,相当于资源观察者的
	个数,都是定义成基于CAS操作的原子类型,增减引用计数时时
	线程安全的操作
	*/
	_Atomic_counter_t _Uses;
	_Atomic_counter_t _Weaks;
}

所以我们可以直到一个shared_ptr < int >ptr(new int);智能指针对象的内存分布如下
在这里插入图片描述

如果运行如下的代码那么use_count得到的计数是2,出函数作用域析构也只会析构一次,是正确的。这是因为shared_ptr< int >ptr2(ptr1)调用的是拷贝构造函数,只进行了资源的引用计数的改变,没有额外分配其他资源。(类似于浅拷贝)

shared_ptr<int> ptr1(new int);
shared_ptr<int> ptr2(ptr1);
cout<<ptr1.use_count()<<endl;
cout<<ptr2.use_count()<<endl;

在这里插入图片描述
但是当运行如下的代码操作时就有问题了,因为ptr1和ptr2都调用了shared_ptr的构造函数,都重新开辟了引用计数的资源,导致ptr1和ptr2都记录了一次new int计数,所以析构的时候两个ptr都去释放内存资源.

int *p = new int;
shared_ptr<int> ptr1(p);
shared_ptr<int> ptr2(p);
cout<<ptr1.use_count()<<endl;
cout<<ptr2.use_count()<<endl;

在这里插入图片描述

解决办法

  1. 产生同一个资源的多个shared_ptr的时候,通过拷贝构造或者赋值操作符进行操作,不要重新调用构造函数,避免产生多个引用计数资源。
	A *p = new A(); // 裸指针指向堆上的对象
	shared_ptr<A> ptr1(p);// 用shared_ptr智能指针管理指针p指向的对象
	shared_ptr<A> ptr2(ptr1);// 用ptr1拷贝构造ptr2
  1. 使用enable_shared_from_this和shared_from_this机制
    在多线程编程中有时候想在类里面提供一些方法,返回当前对象的一个shared_ptr强智能指针,做参数传递使用。首先肯定不能像之前所写的那样直接返回,这样会直接调用构造函数,还是建立了新的引用计数对象。
    return shared_ptr<A>(this);
    
    那如果一个类需要实现一个函数接口来返回当前类对象的shared_ptr强智能指针怎么办?方法就是继承enable_shared_from_this类,然后通过调用基类继承来的shared_from_this()方法返回一个指向同一个资源对象的智能指针
    #include <iostream>
    using namespace std;
    // 智能指针测试类,继承enable_shared_from_this类
    class A : public enable_shared_from_this<A>
    {
    public:
    	A() :mptr(new int)
    	{
    		cout << "A()" << endl;
    	}
    	~A()
    	{
    		cout << "~A()" << endl;
    		delete mptr;
    		mptr = nullptr;
    	}
    
    	// A类提供了一个成员方法,返回指向自身对象的shared_ptr智能指针
    	shared_ptr<A> getSharedPtr()
    	{
    		/*通过调用基类的shared_from_this方法得到一个指向当前对象的
    		智能指针*/
    		return shared_from_this();
    	}
    private:
    	int *mptr;
    };
    

源码实现分析

  1. 首先一个类继承enable_shared_from_this会怎么样?首先看看基类成员变量是什么

    template<class _Ty>
    	class enable_shared_from_this
    	{	// provide member functions that create shared_ptr to this
    public:
    	using _Esft_type = enable_shared_from_this;
    
    	_NODISCARD shared_ptr<_Ty> shared_from_this()
    		{	// return shared_ptr
    		return (shared_ptr<_Ty>(_Wptr));
    		}
    	// 成员变量是一个指向资源的弱智能指针
    	mutable weak_ptr<_Ty> _Wptr;
    };
    

    当一个类继承了enable_shared_from_this之后,会从基类继承一个成员变量_Wptr,当定义第一个智能指针对象的时候,当定义第一个智能指针对象的时候shared_ptr< A > ptr1(new A()),调用shared_ptr的普通构造函数,就会初始化A对象的成员变量_Wptr,作为观察A对象资源的一个弱智能指针观察者然后代码如下调用shared_ptr< A > ptr2 = ptr1->getSharedPtr(),getSharedPtr函数内部调用shared_from_this()函数返回指向该对象的智能指针,这个函数怎么实现的呢,看源码:

    shared_ptr<_Ty> shared_from_this()
    {	// return shared_ptr
    return (shared_ptr<_Ty>(_Wptr));
    }
    

    shared_ptr< _Ty >(_Wptr),说明通过当前A对象的成员变量_Wptr构造一个shared_ptr出来,看看shared_ptr相应的构造函数:

    shared_ptr(const weak_ptr<_Ty2>& _Other)
    {	// construct shared_ptr object that owns resource *_Other
    	if (!this->_Construct_from_weak(_Other)) // 从弱智能指针提升一个强智能指针
    	{
    	_THROW(bad_weak_ptr{});
    	}
    }
    

    接着看上面调用的_Construct_from_weak方法的实现如下:

    template<class _Ty2>
    bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other)
    {	// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
    // if通过判断资源的引用计数是否还在,判定对象的存活状态,对象存活,提升成功;
    // 对象析构,提升失败!之前的博客内容讲过这些知识,可以去参考!
    if (_Other._Rep && _Other._Rep->_Incref_nz())
    	{
    	_Ptr = _Other._Ptr;
    	_Rep = _Other._Rep;
    	return (true);
    	}
    
    return (false);
    }
    

    综上所说,所有过程都没有再使用shared_ptr的普通构造函数,没有在产生额外的引用计数对象,不会存在把一个内存资源,进行多次计数的过程;更关键的是,通过weak_ptr到shared_ptr的提升,还可以在多线程环境中判断对象是否存活或者已经析构释放,在多线程环境中是很安全的,通过this裸指针进行构造shared_ptr,不仅仅资源会多次释放,而且在多线程环境中也不确定this指向的对象是否还存活。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`std::enable_shared_from_this` 是一个模板类,其目的是为了解决在一个对象中保存 shared_ptr 的问题。它是 C++11 引入的一个特性。 在使用 `std::shared_ptr` 时,我们往往需要在对象中保存一个 `std::shared_ptr` 的副本,这样才能确保对象在使用完毕后不会被提前销毁。但是这种方式会导致一些问题,比如我们无法防止用户直接使用裸指针来操作对象,从而导致对象被提前销毁等问题。 这时候,我们可以使用 `std::enable_shared_from_this` 来解决这些问题。具体而言,我们需要继承 `std::enable_shared_from_this`,然后在对象中使用 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。 下面是一个示例代码: ```c++ #include <iostream> #include <memory> class MyClass : public std::enable_shared_from_this<MyClass> { public: std::shared_ptr<MyClass> get_shared_ptr() { return shared_from_this(); } }; int main() { std::shared_ptr<MyClass> p(new MyClass); std::shared_ptr<MyClass> q = p->get_shared_ptr(); std::cout << "p.use_count() = " << p.use_count() << std::endl; std::cout << "q.use_count() = " << q.use_count() << std::endl; return 0; } ``` 在这个示例中,我们定义了一个名为 `MyClass` 的类,并且继承了 `std::enable_shared_from_this`。然后,我们在类中定义了一个名为 `get_shared_ptr()` 的方法,该方法使用了 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。在 `main()` 函数中,我们先创建了一个 `std::shared_ptr` 对象 `p`,然后通过 `p` 调用 `get_shared_ptr()` 方法获取了一个指向同一个对象的 `std::shared_ptr` 对象 `q`。最后,我们输出了 `p` 和 `q` 的引用计数,可以看到它们的引用计数都是 2。 需要注意的是,在使用 `std::enable_shared_from_this` 时,我们需要确保对象已经被一个 `std::shared_ptr` 管理,否则使用 `shared_from_this()` 方法会导致程序崩溃。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值