一、shared_ptr的分析使用
做出一个像Java中的垃圾回收器,并可以运用的所有资源,heap内存和系统资源都可以使用的系统。
std::shared_ptr就是C++11为了达到上述目标推出的方式。
shared_ptr实现了共享所有权方式来管理资源对象,这意味这没有特定的shared_ptr拥有资源对象。相反,这些指向同一个资源对象的shared_ptr相互协作来确保该资源对象在不需要的时候被析构。
1、特点
1、基于共享所有权模式:多个指针能够同时指向一个资源。
2、基于共享所有权,使用引用计数控制资源对象的生命周期。
3、提供拷贝构造函数和赋值重载函数,提供移动构造和移动赋值函数。
4、为了实现单个对象和一组对象的管理,添加了删除器类型。
5、在容器内保存shared_ptr对象是安全的。
6、shared_ptr重载了operator->和 operator*运算符,因此它可以像普通指针一样使用。
2、引用计数器的作用
1、当新的shared_ptr对象与资源对象的地址关联时,则在其构造函数中,将于此资源关联的引用计数加1.
2、当任何shared_ptr对象超出作用时,则在器析构函数中,将与资源对象关联的引用计数变为0,则表示没有其他shared_ptr对象与此资源对象关联,在这种情况下榻使用delete删除器删除该资源对象。
二、shared_ptr的结构
共享智能指针可以理解为:
shared_ptr指向一个引用计数的结构,这个结构里有一个引用计数还有一个指向目标对象的指针。
示例
class Object
{
private:
int num;
public:
Object(int x=0):num(x){}
~Object() {}
int& value()
{
return num;
}
const int& value()const
{
return num;
}
};
int main()
{
std::shared_ptr<Object>a1(new Object(10));
return 0;
}
可以简单理解为:
当两个指针指向同一个对象时,那么指向的引用计数就加1。
int main()
{
shared_ptr<Object>a1(new Object(10));
shared_ptr<Object>a2(a1);
return 0;
}
在运行过程中可以看到,当那一个智能指针构造另一个智能指针时,两个指针除了本身地址不同,其余都是指向一套东西。切引用计数加一变为二,表示该资源有两个指针共享。
二、创建shared_ptr实例
1.最安全最高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的shared_ptr实例。
2.如果不使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递个shared_ptr的构造函数。
int main()
{
shared_ptr<Object>a1=make_shared<Object>(10);
Object* num=new Object(10);
shared_ptr<Object> a2(num);
return 0;
}
3.检查引用计数
shared_ptr提供两个函数来检查其共享的引用计数分别是uniqu()和use_count()。
use_count()函数用来测试该shared_ptr是否是原始指针的我唯一拥有者,也就是use_count()返回值为1时返回true,否则返回false。
int main()
{
std::shared_ptr<Object>a1 = std::make_shared<Object>(10);
Object* num = new Object(10);
std::shared_ptr<Object>a2(num);
cout << a1.use_count() << endl;
cout << a2.unique() << endl;
return 0;
}
三、仿写shared_ptr
1.和my_unqiue_ptr删除器相同,可以直接使用
template<class _Ty>
class myDeletor
{
public:
myDeletor() = dafault;
void operator()(_Ty* ptr) const
{
if (ptr != nullptr)
{
delete ptr;
}
}
};//删除单个对象
template<class _Ty>
class myDeletor<_Ty[]>
{
public:
myDeletor() = default;
void operator()(_Ty* ptr) const
{
if (ptr != nullptr)
{
delete[]ptr;
}
}
};//删除一组对象
2.引用计数类
template<class _Ty>
class RefCnt
{
private:
_Ty* mptr;//指向对象的指针
int ref;
public:
RefCnt(_Ty* mptr = nullptr) :mptr(p), ref(myptr != nullptr)
{}
~RefCnt()
{}
};
3.指向单独对象的my_shared_ptr
这个类的成员变量有一个指向引用计数的指针和一个删除器组成。
在构造函数中,判断其初始化,如果初始化为nullptr,那就什么都不做,如果不为nullptr,就在堆区申请一个引用计数类。
template<class _Ty,class _Dx=MyDelete<_Ty>>
class my_shared_ptr
{
public:
my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
{
if (p != nullptr)
{
ptr = new RefCnt(p);
}
}
private:
RefCnt<_Ty>* ptr;
_Dx mydeletor;
};
4.拷贝构造函数
shared_ptr拷贝构造时直接将智能指针的指针成员赋值给当前this的指针,再判断是否为nullptr,
若不为nullptr,则引用计数加一。
my_shared_ptr(const my_shared_ptr& _Y) :ptr(_Y.ptr)
{
if (ptr != nullptr)
{
ptr->ref +=1;
}
}
5.移动构造函数
移动构造顾名思义就是转移资源,所以无需其他操作,让当前this指针成员也指向参数所指的引用计数,让参数的指针置为nullptr。
my_shared_ptr(const myshared_ptr&& _Y) :ptr(_Y.ptr)
{
_Y.ptr = nullptr;
}
6.重载bool运算符
operator bool() const
{
return ptr!=nullptr;
}
7.赋值构造函数
my_shared_ptr& operator=(const my_shared_ptr& _Y)
{
if (this == &_Y || this->ptr == _Y.ptr)return *this;
if (ptr != NULL && --ptr->ref == 0)
{
mydeletor(ptr);
}
ptr = _Y.ptr;
if (ptr != nullptr)
{
ptr->ref += 1;
}
return *this;
}
shared_ptr满足下列几种情况。
1.当自己个自己赋值时直接返回。
2.在赋值前要先将自生的资源移交出去,即就是将自身指向的引用计数减一,如果减一后引用计数为0,那么就调用删除器将资源删除,表示没有指针指向当前资源。之后再将参数的指针赋值给当前指针,如果当前指针不为nullptr,引用计数加一。简单理解如下图
3.当拿一个空指针赋给有资源的函数时,自身引用计数建议,若减完为0,则调删除器,拿空指针赋值。简单理解如下图
8.移动赋值函数
my_shared_ptr& operator=(my_shared_ptr&& _Y)
{
if (this == &_Y)return *this;
if (this->ptr == _Y.ptr && this->ptr != nullptr)
{
this->ptr->ref -= 1;
_Y.ptr = nullptr;
return *this;
}
if (this->ptr != nullptr && --ptr->ref == 0)
{
mydeletor(ptr);
}
ptr = _Y.ptr;
_Y.ptr = nullptr;
return *this;
}
同样满足下列几种情况
1.自赋值情况
2.两个指针指向相同的情况,如果指向相同,那么引用计数减一,再将参数的指针置为空。
3.若当前指针不为空且引用计数减一后为0,调用删除器释放资源对象,再用参数对其赋值、
4.若当前指指针指向空,则直接将当前指着置为空。
9.reset()函数
将指针重新指向其他资源
1.若当前指针不为空,且引用计数减一后等于0,此时调用删除器,如果引用计数减一不为0,’
说明还有其他指针指向参数类型对象。
2.指针指向重新向堆区申请引用计数的结构,该结构的指针指向参数类型的对象。
void reset(_Ty* p=nullptr)
{
if (this->ptr != nullptr && --ptr->ref == 0)
{
mydeletor(ptr);
}
ptr = new RefCnt<_Ty>(p);
}
10.析构函数
析构的是当前指针,先判断是否为空,再引用计数减一,如果结果为0,则调用删除器先删除引用计数所致的对象,在删除指针。将当前指针置为空。
~my_shared_ptr()
{
if (this->ptr != nullprtr && --ptr->ref == 0)
{
mydeletor(ptr->mptr);
delete ptr;
}
ptr = nullptr;
}
11.观察器:重载解引用和指向符
_Ty* get()const { return ptr->mptr; }
_Ty& operator*() const
{
return *get();
}
_Ty* operator->() const
{
return get();
}
12.获得当前引用计数的个数
即查看当前对象有多少个智能指针共享
size_t use_count() const
{
if (this->ptr == nullptr)return 0;
return this->ptr->ref;
}
13.交换资源函数
void swap(my_shared_ptr& r)
{
std::swap(this->ptr, r.ptr);
}
四、指向一组对象的shared_pt
template<class _Ty,class _Dx>
class my_shared_ptr<_Ty[], _Dx>
{
private:
_Ty* ptr;
_Dx mydeletor;
public:
my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
{
if (p != nullptr)
{
ptr = new RefCnt(p);
}
}
};
相应的删除器操作都需要改变:先将指针所指向的引用计数指针传递给删除器,再删除当前指针。
if(ptr!=nullptr && --ptr->ref==0)
{
mydeletor(ptr->mptr);
delete ptr;
}
_Ty& operator[](const int idx) const
{
return ptr->mptr[idx];
}