一.shared_ptr和weak_ptr的介绍
因为unique_ptr没办法实现对一块空间进行多次指向,所以就产生了shared_ptr,这样就表明其实shared_ptr可以多次指向同一块空间,但是它本身又存在一点点内存释放的缺陷,所以就有了waek_ptr和它一起工作。
二.shared_ptr的实现
通过上一个的unique_ptr我们知道,它本身不能多次指向同一块空间的原因是因为它没办法在析构时得知这块空间是否已经被释放,如果将释放的空间再进行释放,或者释放着块空间以后其他指针还在指向这块空间时,就会出问题,因此,我们本质上就是要解决析构函数要知道有多少指针还在指向这块空间,如果只有最后一个,那么析构就将这块空间释放,但是如果不是最后一个指针,那么就只需要将最后将对应的指针给置为nullptr即可,根据这个思路,我们便想到采用引用计数的方法,但是为了使得同一块空间被多个不同的对象所记录,我们可以采用指针的方式来管理这个引用计数,当指向一块新空间时,就使用new将引用计数指向1,然后后面就依次管理这个引用计数就可以了。所以,这个对象的成员变量肯定是一个指针和一个引用计数指针
template <typename T>
class sherad_ptr
{
private:
T* _ptr;
std::atomic<int>* _count;
然后就是一般的构造函数了,这里我们知道既然要使用引用计数,所以就在默认构造时将引用计数初始化就行了
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
,_count(new int(1))
{
if (!ptr)
{
delete _count;
_count = nullptr;
}
}
而移动拷贝函数,和移动拷贝构造函数基本上和unique_ptr是一至的,就不多废话了
shared_ptr(shared_ptr<T>&& other)
:_ptr(other._ptr)
, _count(other._count)
{
if (other._ptr)
{
other._ptr = nullptr;
other._count = nullptr;
}
}
shared_ptr& operator = (shared_ptr&& other)
{
if (_ptr != other._ptr)
{
release();
_ptr = other._ptr;
_count = other._count;
other._ptr = nullptr;
other._count = nullptr;
}
}
对于普通的拷贝构造函数和拷贝函数来说,这里就不需要禁止了,只需要管理好引用计数的问题,首先对于拷贝构造函数来说,就是将对方的_ptr和_count进行拷贝过来,然后判断是不是空指针,若不是就将引用计数进行++,而对于拷贝函数来说,要多一步将原本的指针进行释放,如果现在的指针和拷贝的指针不一样的话
shared_ptr(shared_ptr<T>& other)
:_ptr(other._ptr)
,_count(other._count)
{
if(ptr)
*_count++;
}
shared_ptr<T>& operator =(shared_ptr<T>& other)
{
if (this->_ptr != other._ptr)
{
release();
this->_ptr = other._ptr;
_count = other._count;
if(ptr)
*_count++;
}
return *this;
}
对于析构函数来说,就是判断引用计数是否为1,如果不是,就减减引用计数将指针指向nullptr就行了,但如果是,就需要释放空间和引用计数;
void release()
{
if (*_count != 1 && _ptr != nullptr)
{
*_count--;
_ptr = nullptr;
}
else
{
delete _ptr;
delete _count;
}
}
剩下的就是一些基础的函数了,和unique_ptr区别不大
T operator *()
{
return *_ptr;
}
T* operator ->()
{
return _ptr;
}
T* get() const { return ptr; }
// 检查是否为空
bool operator!() const { return ptr == nullptr; }
bool operator==(std::nullptr_t) const { return ptr == nullptr; }
bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
// 获取引用计数
long use_count() const {
return count ? count->load() : 0;
}