继续讲解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了,就认为计数成员这块就没人用了,直接析构掉了。