C++之智能指针

1.智能指针的作用

手动申请的资源,如果没释放就会导致内存泄漏,为了预防内存泄露,可以通过智能指针管理资源,让智能指针对象在指针生命周期结束时主动释放资源。

2.智能指针的原理

(1)RAII特性;
(2)重载operator*和operator->,用对象模拟原生指针的行为

RALL特性

RAII是什么?
一种利用对象生命周期来控制程序资源(如内存,文件句柄,套接字,互斥量等等)的简单技术
RAII是怎样做的?
在对象构造时获取资源,控制对资源的访问使之在对象的生命周期内始终有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的任务交给了一个对象。
RAII的好处:
不需要显示地释放资源;
通过采用这种方式,对象所需的资源在生命周期内始终保持有效。

RAII设计守卫锁

class LockGuard
{
public:
	 LockGuard(Mutex& mtx)
		 :_mutex(mtx)
	 {
		 _mutex.lock();
	 }
	 ~LockGuard()
	 {
		 _mutex.unlock();
	 }
	 LockGuard(const LockGuard<Mutex>&) = delete;
private:
	 // 注意这里必须使用引用,否则锁的就不是一个互斥量对象
	 Mutex& _mutex;
};

通过RAII思想设计的智能指针类

template<class T>
class SmartPtr {
public:
 SmartPtr(T* ptr = nullptr)
	 : _ptr(ptr)
 {}
 ~SmartPtr()
 {
	 if(_ptr)
	 	delete _ptr;
 }
 T& operator*() {return *_ptr;}
 T* operator->() {return _ptr;}
private:
 T* _ptr;
};

3.智能指针的特点

指针生命周期结束时主动释放资源;
杜绝指针运算和指针比较;

4.常见的智能指针

(1)auto_ptr

实现原理:
管理权转移,也就是当把一个auto_ptr对象ap中的内容拷贝到另一个auto_ptr对象中时,会将ap和它所管理的资源断开联系,将资源转移到当前对象中
因为auto_ptr的资源会转移,所以禁止使用该智能指针

模拟实现auto_ptr

//一个会在拷贝和赋值时资源转移的智能指针类
template<class T>
class Auto_ptr
{
public:
	Auto_ptr(T* ptr)
		:_ptr(ptr)
	{}

	Auto_ptr(const Auto_ptr& obj)
	{
		//资源转移
		_ptr = obj._ptr;
		const_cast<Auto_ptr<T>&>(obj)._ptr = nullptr;
	}

	Auto_ptr& operator=(const Auto_ptr& obj)
	{
		if (this != &obj)
		{
			//释放原空间
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}

			//资源转移
			_ptr = obj._ptr;
			const_cast<Auto_ptr<T>&>(obj)._ptr = nullptr;
		}
	}

	~Auto_ptr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

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

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

	//判断资源是否存在
	bool isNull()
	{
		return _ptr == nullptr;
	}

	T* getPtr()
	{
		return _ptr;
	}
//声明为protected可以让在被继承时
//让基类的成员不能在类外被访问,只能在派生类中被访问
protected:
	T* _ptr = nullptr;
};

(2)unique_ptr

实现原理:防拷贝

模拟实现

template<typename T>
class UniquePtr{
public:
	UniquePtr(T* ptr)
		:_ptr(ptr)
	{}
	~UniquePtr()
	{
		if (_ptr)
			delete _ptr;
	}
	//防拷贝
	UniquePtr(UniquePtr& obj) = delete;
	UniquePtr& operator=(UniquePtr& obj) = delete;
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

(3)shared_ptr

实现原理

通过引用计数的方式来实现对多个shared_ptr对象之间资源的共享

模拟实现

计数器加1和资源的释放都是非原子操作,存在线程安全问题,所以需要加锁保护。

template<class T>
class SharedPtr{
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(new int(1))
		, _mutex(new mutex)
	{}
	~SharedPtr()
	{
		Release();
	}
	SharedPtr(SharedPtr& obj)
		:_ptr(obj._ptr)
		, _count(obj._count)
		, _mutex(obj._mutex)
	{
		//计数++
		AddCount();
	}
	SharedPtr& operator=(SharedPtr& obj)
	{
		//if (this != &obj)
		if(_ptr != obj._ptr)
		{
			//释放旧资源
			Release();
			//共享管理的资源
			_ptr = obj._ptr;
			_count = obj._count;
			_mutex = obj._mutex;

			//计数++
			AddCount();
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int UseCount()
	{
		return *_count;
	}
	T* getPtr()
	{
		return _ptr;
	}
private:
	T* _ptr;		//管理的资源
	int* _count;	//计数器
	mutex* _mutex;	//互斥锁

	void AddCount()
	{
		_mutex->lock();
		++(*_count);
		_mutex->unlock();
	}
	void Release()
	{
		bool flag = false;//是否计数器变为0
		_mutex->lock();
		if (--(*_count) == 0)
		{
			delete _ptr;
			delete _count;
			_ptr = nullptr;
			_count = nullptr;
			flag = true;
		}
		_mutex->unlock();
		if (flag == true)
		{
			delete  _mutex;
			_mutex = nullptr;
		}
	}
};

shared_ptr存在的问题:循环引用

当两个对象中有智能指针成员且相互指向时,在调用析构函数后计数-1,但是计数没有减到0,他在等待另外一个智能指针对象释放资源才释放资源,但是另一个对象也在等待该对象释放资源,双方陷入一种类似死锁的场景,可以通过使用weak_ptr解决,weak_ptr作为智能指针成员时,即使相互指向也不会增加引用计数,因此释放资源时不会受影响。

自定义资源释放方式:删除器

自定义释放空间的方式,通过定义一个仿函数类,仿函数类中指定释放空间的方式
删除器示例:

template<class T>
struct DeleteArrDel
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
加入删除器的SharedPtr
template<class T>
struct DeleteDel
{
	void operator()(T* ptr)
	{
		delete ptr;
	}
};

template<class T>
struct FreeDel
{
	void operator()(T* ptr)
	{
		free(ptr);
	}
};

template<class T>
struct DeleteArrDel
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};

//Del:删除器(定义删除数据的格式)
template<class T,class Del=DeleteDel<T>>
class SharedPtr{
public:
	

	SharedPtr(T* ptr = nullptr,Del del=DeleteDel<T>)
		:_ptr(ptr)
		, _count(new int(1))
		, _mutex(new mutex)
		, _del(del)
	{}


	~SharedPtr()
	{
		Release();
	}
	SharedPtr(SharedPtr& obj)
		:_ptr(obj._ptr)
		, _count(obj._count)
		, _mutex(obj._mutex)
	{
		//计数++
		AddCount();
	}
	SharedPtr& operator=(SharedPtr& obj)
	{
		//if (this != &obj)
		if (_ptr != obj._ptr)
		{
			//释放旧资源
			Release();
			//共享管理的资源
			_ptr = obj._ptr;
			_count = obj._count;
			_mutex = obj._mutex;

			//计数++
			AddCount();
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int UseCount()
	{
		return *_count;
	}
	T* getPtr()
	{
		return _ptr;
	}
private:
	T* _ptr;		//管理的资源
	int* _count;	//计数器
	mutex* _mutex;	//互斥锁
	Del _del;		//删除器

	void AddCount()
	{
		_mutex->lock();
		++(*_count);
		_mutex->unlock();
	}
	void Release()
	{
		bool flag = false;//是否计数器变为0
		_mutex->lock();
		if (--(*_count) == 0)
		{
			//delete _ptr;
			//通过删除器释放空间
			_del(_ptr);
			delete _count;
			_ptr = nullptr;
			_count = nullptr;
			flag = true;
		}
		_mutex->unlock();
		if (flag == true)
		{
			delete  _mutex;
			_mutex = nullptr;
		}
	}
};

删除器使用示例:

struct A{
	int _a;
	A()
		:_a(1024)
	{}
	~A()
	{
		cout << "~A()" << endl;
	}
};
void test()
{
	SharedPtr<A,DeleteArrDel<A>> ptr(new A[10],DeleteArrDel<A>());
}

测试结果:
在这里插入图片描述

4.C++和boost中的智能指针的关系

(1) C++ 98 中产生了第一个智能指针auto_ptr.
(2)C++ boost给出了更实用的scoped_ptr、shared_ptr和weak_ptr.
(3)C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
(4)C++ 11,引入了unique_ptr、shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值