175-C++学习第十五弹(共享型,弱智能指针)

继续讲解shared_ptr智能指针

在这里插入图片描述

左右两部分的区别在哪里?

右部分的移动赋值需要把objb的资源释放,左部分是普通赋值,则是objc和objb共享objb的资源。右部分obja移动构造objb,obja的资源会释放掉,而左部分obja拷贝构造objb,两者共享obja的资源。

左部分构造完obja,objb,objc的代码图解如下:
在这里插入图片描述
执行objc=objb;后如下
在这里插入图片描述
这3个智能指针指向同一个对象,引用计数为3
当 return 0;主函数要结束,按照先进后出的原则,先析构objc对象,引用计数减为2,把objc的mPtr,mpRefCnt的指针置为空,释放这个对象。然后析构objb对象,引用计数减为1,把objb的mPtr,mpRefCnt的指针置为空,释放这个对象。然后析构obja对象,引用计数减为0,析构的时候连同调用Object的析构函数,还要把指针计数成员指向堆区的资源也释放掉,然后把mPtr,mpRefCnt置为空,obja的生存期到期。

右部分构造完obja,objb,objc对象的代码图解如下:
在这里插入图片描述

移动构造
在这里插入图片描述

执行objc=std::move(objb);后如下

在这里插入图片描述
引用计数仍然为1
析构objc对象,引用计数减为0,把对象Object和指针计数成员指向的堆区资源释放掉,把objc的mPtr,mpReCnt都置为空,objb,obja对象的指针成员均为空空,不做任何处理。

计数器的实现
在这里插入图片描述

增加返回当前引用计数值的函数
在这里插入图片描述
在这里插入图片描述

删除器
在这里插入图片描述

shared_ptr智能指针
在这里插入图片描述
在这里插入图片描述

如果构建的mPtr是空指针,我们什么都不去做
在这里插入图片描述

如果是拷贝构造
在这里插入图片描述

析构pobjb对象,我们需要判断mPtr是否指向对象Object,才能对引用计数减1
如下面这种情况
在这里插入图片描述
我们在析构pobjc的时候,这两个指针都是空指针,我们进入析构函数的这条语句时
在这里插入图片描述
造成了崩溃
在这里插入图片描述

修改析构函数

在这里插入图片描述

重写赋值语句重载(完美版)

shared_ptr& operator=(const shared_ptr& src)
{
    
    
	if (this == &src || this->mPtr == src.mPtr) return *this;
	//两者均指向同一个资源
	
	if (mPtr != nullptr && 0 == mpRefCnt->delRef())
	{
    
    
		mDeletor(mPtr);
		mPtr = nullptr;
		delete mpRefCnt;
		mpRefCnt = nullptr;
	}
	mPtr = src.mPtr;
	mpRefCnt = src.mpRefCnt;
	if (mPtr != nullptr)
	{
    
    
		mpRefCnt->addRef();
	}
	return *this;
}

解决了
1、空 不空
2、空 空
3、不空
4、不空 不空
5、两者均指向同一个资源
即下面这种情况

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

移动构造

在这里插入图片描述

移动赋值

在这里插入图片描述

返回当前对象的引用计数的值

在这里插入图片描述
重载bool()
在这里插入图片描述

重写 reset()

在这里插入图片描述
以下是解析:
在这里插入图片描述
构建pobja
在这里插入图片描述
构建pbjb
在这里插入图片描述
pobja调用reset语句
在这里插入图片描述
如果是下面这种情况呢?
在这里插入图片描述
此情况下的引用计数指向并没有改变
所以我们需要 mpRefCnt=nullptr;这句代码

swap函数

在这里插入图片描述

处理数组

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多线程问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
示例1:
在这里插入图片描述
在这里插入图片描述
中间有可能被打断

示例2:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
虽然在引用计数上是安全的
但是在赋值,拷贝构造,移动赋值,移动构造一定要加锁

其他问题

循环引用

在这里插入图片描述
示例:
在这里插入图片描述
画图如下:
构建par
在这里插入图片描述
构建pch
在这里插入图片描述
执行par->c=pch;
在这里插入图片描述
执行pch->p=par;
在这里插入图片描述
造成了循环引用
当我们return的时候
我们要析构pch,pch析构的时候,发现引用计数=2,把引用计数变为1,把mPr和mpRefCnt置为空,析构par,发现引用计数=2,把引用计数变为1,把mPtr和mpRefCnt置为空在这里插入图片描述
剩下2个对象互相不能被释放
形成循环引用,导致对象不能被释放。
在这里插入图片描述

析构函数无法调动
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了解决此问题,C++11引入weak_ptr来打破这种循环引用

weak_ptr智能指针

伺候共享型智能指针
在这里插入图片描述
在这里插入图片描述
示例:
Object类
在这里插入图片描述
在这里插入图片描述
上图的最后一行代码的写法是错误的哦!
在这里插入图片描述
在这里插入图片描述
弱指针不占引用计数值,可以检查对象是否存在
在这里插入图片描述
在这里插入图片描述
但是
如果放弃对象
在这里插入图片描述
在这里插入图片描述
资源已被释放

弱指针只是标志对象的存在与否,并不会使引用计数加1

在这里插入图片描述
在这里插入图片描述

使用weak_ptr解决刚才的父子循环引用问题

在这里插入图片描述
在这里插入图片描述
打印结果如下:
在这里插入图片描述
在这里插入图片描述

如何实现weak_ptr智能指针?

#include<iostream>
#include<new>
#include<stdio.h>
#include<atomic> 
using namespace std;

class Object
{
    
    
private:
	int value;
public:
	Object(int x = 0) :value(x) {
    
     cout << "construct object" << endl; }
	~Object() {
    
     cout << "deconstruct object" << endl; }
	int& Value() {
    
     return value; }
	const int& Value() const {
    
     return value; }
};

namespace yhp
{
    
    
	template<class T>
	class RefCnt
	{
    
    
	private:
		T* mPtr;
		std::atomic<int> mCnt_s;	   // 对shared_ptr工作 
		std::atomic<int> mCnt_w;       // 对weak_ptr工作 
	public:
		RefCnt(T* p = nullptr) : mPtr(p) 
		{
    
    
			if (mPtr != nullptr)
			{
    
    
				mCnt_s = 1;
				mCnt_w = 0; 
			}
		}
		void addRef_s() {
    
     mCnt_s++; }
		int delRef_s() {
    
     return --mCnt_s; }
		int getCnt_s() const {
    
     return mCnt_s.load(); }

		void addRef_w() {
    
      mCnt_w++; }
		int delRef_w() {
    
     return --mCnt_w; }
		int getCnt_w() const {
    
     return mCnt_w; }

	};

	template<typename T>
	struct MyDeletor
	{
    
    
		void operator()(T* ptr) const
		{
    
    
			delete ptr;
		}
	};

	template<class T> class weak_ptr;//声明 

	template<typename T, typename Deletor = MyDeletor<T> >
	class shared_ptr
	{
    
    
	public:
		shared_ptr(T* ptr = nullptr) :mPtr(ptr), mpRefCnt(nullptr)
		{
    
    
			if (mPtr != nullptr)
			{
    
    
				mpRefCnt = new RefCnt<T>(ptr);
			}
		}
		~shared_ptr()
		{
    
    
			if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
			{
    
    
				mDeletor(mPtr);
				delete mpRefCnt;
			}
			mPtr = nullptr;
			mpRefCnt = nullptr;
		}
		T& operator*() const {
    
     return *mPtr; }
		T* operator->() const {
    
     return mPtr; }

		shared_ptr(const shared_ptr& src)
			:mPtr(src.mPtr), mpRefCnt(src.mpRefCnt)
		{
    
    
			if (mPtr != nullptr)
			{
    
    
				mpRefCnt->addRef_s();
			}
		}
		shared_ptr& operator=(const shared_ptr& src)
		{
    
    
			if (this == &src || this->mPtr == src.mPtr) return *this;
			if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
			{
    
    
				mDeletor(mPtr);
				mPtr = nullptr;
				delete mpRefCnt;
				mpRefCnt = nullptr;
			}
			mPtr = src.mPtr;
			mpRefCnt = src.mpRefCnt;
			if (mPtr != nullptr)
			{
    
    
				mpRefCnt->addRef_s();
			}
			return *this;
		}

		shared_ptr(shared_ptr&& _u)
		{
    
    
			mPtr = _u.mPtr;
			mpRefCnt = _u.mpRefCnt;
			_u.mPtr = nullptr;
			_u.mpRefCnt = nullptr;
		}
		shared_ptr& operator=(shared_ptr&& _u)
		{
    
    
			if (this == &_u) return *this;
			if (this->mPtr != nullptr && 0 == mpRefCnt->delRef_s())
			{
    
    
				mDeletor(mPtr);
				mPtr = nullptr;
				delete mpRefCnt;
				mpRefCnt = nullptr;

			}
			mPtr = _u.mPtr;
			mpRefCnt = _u.mpRefCnt;
		}
		int use_count() const
		{
    
    
			int cnt = 0;
			if (mpRefCnt != nullptr)
			{
    
    
				cnt = mpRefCnt->getCnt_s();
			}
			return cnt;
		}
		operator bool() const
		{
    
    
			return (mPtr != nullptr);
		}
		void reset(T* ptr)
		{
    
    
			if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
			{
    
    
				mDeletor(mPtr);
				mPtr = nullptr;
				delete mpRefCnt;
				mpRefCnt = nullptr;
			}
			mPtr = ptr;
			mpRefCnt = nullptr;	
			if (mPtr != nullptr)
			{
    
    
				mpRefCnt = new RefCnt<T>(mPtr);
			}
		}
		void swap(shared_ptr& _X)
		{
    
    
			std::swap(this->mPtr, _X.mPtr);
			std::swap(this->mpRefCnt, _X.mpRefCnt);
		}
		shared_ptr(const weak_ptr<T>& _w)
		{
    
    
			mPtr = _w.mPtr;
			mpRefCnt = _w.mpRefCnt;
			mpRefCnt->addRef_s();
		}
		friend class weak_ptr<T>;
	private:
		T* mPtr; // obj , nullptr;
		yhp::RefCnt<T>* mpRefCnt;
		Deletor mDeletor;
	};
	

	template<class T>
	class weak_ptr
	{
    
    
	public:
		weak_ptr() :mPtr(nullptr), mpRefCnt(nullptr) {
    
    }//无参的构造函数 
		weak_ptr(shared_ptr<T>& s) :mPtr(s.mPtr), mpRefCnt(s.mpRefCnt)//共享型智能指针才是带参数的构造函数 
		{
    
    
			if (mpRefCnt != nullptr)
			{
    
    
				mpRefCnt->addRef_w();//观察者引用计数加1 
			}
		}
		~weak_ptr()//析构函数 
		{
    
    
			release();//释放函数 
		}
		weak_ptr(weak_ptr<T>& w) :mPtr(w.mPtr), mpRefCnt(w.mpRefCnt)
		{
    
    
			if (mpRefCnt != nullptr)
			{
    
    
				mpRefCnt->addRef_w();//观察者引用计数加1 
			}
		}
		weak_ptr& operator=(const shared_ptr & s)
		{
    
    
			release();
			mPtr = s.mPtr;
			mpRefCnt = s.mpRefCnt;
			mpRefCnt->addRef_w();//观察者引用计数加1 
		}
		shared_ptr<T> lock() const 
		{
    
    
			return shared_ptr<T>(*this);
		}
		int use_const() const
		{
    
    
			return mpRefCnt->getCnt_s();
		}
		bool expired() const
		{
    
    
			if (mpRefCnt != nullptr)
			{
    
    
				return mpRefCnt->getCnt_s() == 0;
			}
			return true;
		}
		friend class shared_ptr<T>;
	private:
		void release()
		{
    
    
			if (mpRefCnt != nullptr)
			{
    
    
				mpRefCnt->delRef_w();//观察者引用计数减1 
				if (mpRefCnt->getCnt_s() == 0 && mpRefCnt->getCnt_w())
				{
    
    
					delete mpRefCnt;
					mpRefCnt = nullptr;
				}
			}
			mPtr = nullptr;
			mpRefCnt = nullptr;
		}
		T* mPtr; 
		RefCnt<T>* mpRefCnt;
		//无删除器,weak_ptr没有能力对对象进行删除 
	};
}

在这里插入图片描述
在这里插入图片描述
讲解:
在这里插入图片描述
当在主函数创建par对象
在这里插入图片描述
当主函数创建pch对象
在这里插入图片描述
是weak_ptr类型
在这里插入图片描述
当我们执行par->c=pch;
调动弱指针的赋值函数
在这里插入图片描述
当执行pch->p=par;
在这里插入图片描述
当我们销毁pch的时候,真实的拥有者变为0,把指向的堆区对象给析构掉 ,把par的观察者变为0,暂时不析构堆区的计数成员对象
在这里插入图片描述
当我们销毁par的时候,真实的拥有者变为0,析构堆区的对象,把pch的观察者变为0,也就是把pch的堆区的计数成对象也析构了。回过头来,发现自身的计数成员的观察者和真实拥有者均为0,也把自身的堆区的计数成员对象析构掉了。

在这里插入图片描述
在其他地方的智能指针使用的时候,发现真实拥有者为0了,就认为计数成员这块就没人用了,直接析构掉了。

猜你喜欢

转载自blog.csdn.net/LINZEYU666/article/details/113728635