C++11之智能指针(RAII)

为什么需要智能指针?

试想一下场景
在这里插入图片描述
如下:
在这里插入图片描述
这时候只能指针就可以派上用场啦!

智能指针的使用及原理

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
1.不需要显式地释放资源。
2.采用这种方式,对象所需的资源在其生命期内始终保持有效。

使用RAII思想设计的SmartPtr类

template<class T>
class SmartPtr
{
public:
	SmartPtr(T * ptr)
		:_ptr(ptr)//构造时对想要保护的资源指针进行拷贝
	{}
	~SmartPtr()
	{
		cout << "delete ptr: " << _ptr << endl;//为了看效果
		delete _ptr;
	}	
	//像指针一样使用
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
private:
	T * _ptr;
};

在这里插入图片描述
在这里插入图片描述

智能指针历史

C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。

template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			// 管理权转移
			sp._ptr = nullptr;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

在这里插入图片描述

C++11更新的智能指针

C++11中的智能指针有以下几种:
unique_ptr:防止拷贝的智能指针 对应boost库中的scoped_ptr
shared_ptr: 可以拷贝的智能指针
weak_ptr: 解决shared_ptr循环引用

unique_ptr:防止拷贝的智能指针, 简单实现

template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
			system("pause");

		}

		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		unique_ptr(const unique_ptr<T>&) = delete;//防止拷贝
		unique_ptr<T>& operator=(const unique_ptr<T> &) = delete;//防止拷贝
	private:
		T* _ptr;

在这里插入图片描述

需要拷贝的场景shared_ptr

shared_ptr的原理

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
例如:最后一个走的锁门。

1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

shared_ptr的线程安全问题

通过下面的程序我们来测试shared_ptr的线程安全问题。需要注意的是shared_ptr的线程安全分为两方面:
1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。
2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。
3. 指向堆上资源的线程安全问题是访问的人处理的,智能指针不管,也管不了.
4. 引用计数的线程安全问题,是智能指针要处理的

总结:智能指针需要锁保证自己的引用计数安全,其访问资源的线程安全智能指针不管是需要操作者自己处理的

先画图介绍shared_ptr如何复制的机制
在这里插入图片描述
设计的很有意思可以看看代码品一品:

template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T * ptr)
			:_ptr(ptr)
			, _pRefCount(new int (1))
			,_mtx(new mutex)
		{}

		void AddRef()//统一接口便于使用
		{
			_mtx->lock();

			(*_pRefCount)++;

			_mtx->unlock();
		}

		void Release()//统一接口便于使用
		{
			bool flag = false;

			_mtx->lock();

			if (--(*_pRefCount) == 0 && _ptr)//引用计数--, 如果引用计数减为0了再释放资源
			{
				cout << "delete:" << _ptr << endl;
				delete _pRefCount;
				delete _ptr;
				flag = true;
			}

			_mtx->unlock();

			if (flag == true)
				delete _mtx;
		}

		shared_ptr(const shared_ptr<T> & sp)//拷贝构造只需要将引用计数加一
			: _ptr(sp._ptr)
			, _pRefCount(sp._pRefCount)
			, _mtx(sp._mtx)
		{
			AddRef();
		}

		shared_ptr<T>& operator=(const shared_ptr<T> & sp)
		{
			if (_ptr != sp._ptr)
			{
				Release();//将原来的引用计数--
				_ptr = sp._ptr;
				_pRefCount = sp._pRefCount;
				_mtx = sp._mtx;
				AddRef();//将新的引用计数++
			}
			return *this;
		}

		~shared_ptr()
		{
			Release();
		}
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		T* get() const
		{
			return _ptr;
		}

		int use_count()
		{
			return *_pRefCount;
		}

	private:
		T * _ptr;
		int * _pRefCount;//引用计数
		mutex * _mtx;    //保护引用计数的锁
	};

在这里插入图片描述

shared_ptr的循环引用问题

在这里插入图片描述

如何解决循环引用----->weak_ptr

weak_ptr的特点就是不会增加引用计数,他是专门负责解决shared_ptr的循环引用问题而设计的

template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T * ptr)
			:_ptr(ptr)
			, _pRefCount(new int (1))
			,_mtx(new mutex)
		{}

		void AddRef()//统一接口便于使用
		{
			_mtx->lock();

			(*_pRefCount)++;

			_mtx->unlock();
		}

		void Release()//统一接口便于使用
		{
			bool flag = false;

			_mtx->lock();

			if (--(*_pRefCount) == 0 && _ptr)//引用计数--, 如果引用计数减为0了再释放资源
			{
				cout << "delete:" << _ptr << endl;
				delete _pRefCount;
				delete _ptr;
				flag = true;
			}

			_mtx->unlock();

			if (flag == true)
				delete _mtx;
		}

		shared_ptr(const shared_ptr<T> & sp)//拷贝构造只需要将引用计数加一
			: _ptr(sp._ptr)
			, _pRefCount(sp._pRefCount)
			, _mtx(sp._mtx)
		{
			AddRef();
		}

		shared_ptr<T>& operator=(const shared_ptr<T> & sp)
		{
			if (_ptr != sp._ptr)
			{
				Release();//将原来的引用计数--
				_ptr = sp._ptr;
				_pRefCount = sp._pRefCount;
				_mtx = sp._mtx;
				AddRef();//将新的引用计数++
			}
			return *this;
		}

		~shared_ptr()
		{
			Release();
		}
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		T* get() const
		{
			return _ptr;
		}

		int use_count()
		{
			return *_pRefCount;
		}

	private:
		T * _ptr;
		int * _pRefCount;//引用计数
		mutex * _mtx;    //保护引用计数的锁
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();

			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}


	private:
		T* _ptr;
	};

在这里插入图片描述

定制删除器

定制删除器 考试基本不考,实际使用有价值
默认情况,智能指针底层都是delete资源
那么如果你的资源不是new出来的呢?比如:new[]、malloc、fopen
定制删除器 – 可调用对象
因为我们默认的是delete,如果new int[]就需要我们设计专门的仿函数来释放资源,否则会造成资源泄露问题。
在这里插入图片描述

template<class T>
	class default_delete
	{
	public:
		void operator()(const T* ptr)
		{
			cout << "delete:" << ptr << endl;
			delete ptr;
		}
	};

	template<class T, class D = default_delete<T>>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
			{
				/*cout << "delete:" << _ptr << endl;
				delete _ptr;*/
				D del;
				del(_ptr);
			}
			system("pause");

		}

		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		unique_ptr(const unique_ptr<T>&) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T> &) = delete;
	private:
		T* _ptr;
	};

在这里插入图片描述
shared_ptr可以在参数列表定制删除器
在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值