shared_ptr

 一、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];
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值