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

继续讲解shared_ptr智能指针

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

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

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

左部分构造完obja,objb,objc的代码图解如下:
175-C++学习第十五弹(共享型,弱智能指针)
执行objc=objb;后如下
175-C++学习第十五弹(共享型,弱智能指针)
这3个智能指针指向同一个对象,引用计数为3
当 return 0;主函数要结束,按照先进后出的原则,先析构objc对象,引用计数减为2,把objc的mPtr,mpRefCnt的指针置为空,释放这个对象。然后析构objb对象,引用计数减为1,把objb的mPtr,mpRefCnt的指针置为空,释放这个对象。然后析构obja对象,引用计数减为0,析构的时候连同调用Object的析构函数,还要把指针计数成员指向堆区的资源也释放掉,然后把mPtr,mpRefCnt置为空,obja的生存期到期。

右部分构造完obja,objb,objc对象的代码图解如下:
175-C++学习第十五弹(共享型,弱智能指针)

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

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

175-C++学习第十五弹(共享型,弱智能指针)
引用计数仍然为1
析构objc对象,引用计数减为0,把对象Object和指针计数成员指向的堆区资源释放掉,把objc的mPtr,mpReCnt都置为空,objb,obja对象的指针成员均为空空,不做任何处理。

计数器的实现
175-C++学习第十五弹(共享型,弱智能指针)

增加返回当前引用计数值的函数
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
删除器
175-C++学习第十五弹(共享型,弱智能指针)
shared_ptr智能指针
175-C++学习第十五弹(共享型,弱智能指针)

175-C++学习第十五弹(共享型,弱智能指针)
如果构建的mPtr是空指针,我们什么都不去做
175-C++学习第十五弹(共享型,弱智能指针)
如果是拷贝构造
175-C++学习第十五弹(共享型,弱智能指针)
析构pobjb对象,我们需要判断mPtr是否指向对象Object,才能对引用计数减1
如下面这种情况
175-C++学习第十五弹(共享型,弱智能指针)
我们在析构pobjc的时候,这两个指针都是空指针,我们进入析构函数的这条语句时
175-C++学习第十五弹(共享型,弱智能指针)
造成了崩溃
175-C++学习第十五弹(共享型,弱智能指针)

修改析构函数

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

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

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、两者均指向同一个资源
即下面这种情况

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

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

移动构造

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

移动赋值

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

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

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

重写 reset()

175-C++学习第十五弹(共享型,弱智能指针)
以下是解析:
175-C++学习第十五弹(共享型,弱智能指针)
构建pobja
175-C++学习第十五弹(共享型,弱智能指针)
构建pbjb
175-C++学习第十五弹(共享型,弱智能指针)
pobja调用reset语句
175-C++学习第十五弹(共享型,弱智能指针)
如果是下面这种情况呢?
175-C++学习第十五弹(共享型,弱智能指针)
此情况下的引用计数指向并没有改变
所以我们需要 mpRefCnt=nullptr;这句代码

swap函数

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

处理数组

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

多线程问题

175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
示例1:
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
中间有可能被打断

示例2:
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
虽然在引用计数上是安全的
但是在赋值,拷贝构造,移动赋值,移动构造一定要加锁

其他问题

循环引用

175-C++学习第十五弹(共享型,弱智能指针)
示例:
175-C++学习第十五弹(共享型,弱智能指针)
画图如下:
构建par
175-C++学习第十五弹(共享型,弱智能指针)
构建pch
175-C++学习第十五弹(共享型,弱智能指针)
执行par->c=pch;
175-C++学习第十五弹(共享型,弱智能指针)
执行pch->p=par;
175-C++学习第十五弹(共享型,弱智能指针)
造成了循环引用
当我们return的时候
我们要析构pch,pch析构的时候,发现引用计数=2,把引用计数变为1,把mPr和mpRefCnt置为空,析构par,发现引用计数=2,把引用计数变为1,把mPtr和mpRefCnt置为空175-C++学习第十五弹(共享型,弱智能指针)
剩下2个对象互相不能被释放
形成循环引用,导致对象不能被释放。
175-C++学习第十五弹(共享型,弱智能指针)

析构函数无法调动
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
为了解决此问题,C++11引入weak_ptr来打破这种循环引用

weak_ptr智能指针

伺候共享型智能指针
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
示例:
Object类
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
上图的最后一行代码的写法是错误的哦!
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
弱指针不占引用计数值,可以检查对象是否存在
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
但是
如果放弃对象
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
资源已被释放

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

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

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

175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
打印结果如下:
175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)

如何实现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没有能力对对象进行删除 
	};
}

175-C++学习第十五弹(共享型,弱智能指针)
175-C++学习第十五弹(共享型,弱智能指针)
讲解:
175-C++学习第十五弹(共享型,弱智能指针)
当在主函数创建par对象
175-C++学习第十五弹(共享型,弱智能指针)
当主函数创建pch对象
175-C++学习第十五弹(共享型,弱智能指针)
是weak_ptr类型
175-C++学习第十五弹(共享型,弱智能指针)
当我们执行par->c=pch;
调动弱指针的赋值函数
175-C++学习第十五弹(共享型,弱智能指针)
当执行pch->p=par;
175-C++学习第十五弹(共享型,弱智能指针)
当我们销毁pch的时候,真实的拥有者变为0,把指向的堆区对象给析构掉 ,把par的观察者变为0,暂时不析构堆区的计数成员对象
175-C++学习第十五弹(共享型,弱智能指针)
当我们销毁par的时候,真实的拥有者变为0,析构堆区的对象,把pch的观察者变为0,也就是把pch的堆区的计数成对象也析构了。回过头来,发现自身的计数成员的观察者和真实拥有者均为0,也把自身的堆区的计数成员对象析构掉了。

175-C++学习第十五弹(共享型,弱智能指针)
在其他地方的智能指针使用的时候,发现真实拥有者为0了,就认为计数成员这块就没人用了,直接析构掉了。

上一篇:2098-DSD-010X


下一篇:清北灵堂送走记 Day2