C++智能指针

1.为什么需要智能指针?

当我们在代码中申请资源时,方式有多种,malloc出来的就需要free掉, new申请的就需要delete来释放。在这过程中,很容易发生申请资源后忘记释放的情况,这就会导致内存泄漏。不仅如此,如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安全。
而智能指针的存在就可以完美的帮助我们自动释放对象的资源,避免内存的泄露,确保安全。

2.什么是智能指针?

智能指针是C++中预防内存泄漏的方式:
就是让一个类管理原生态指针,自动调用析构函数,用户不需要关心资源何时去释放。
智能指针原理:RAII+类似指针的行为(重载operator()和operator->())+解决浅拷贝

3.智能指针版本

C++98:

auto_ptr(1.0)
RAII+类似指针的行为+解决浅拷贝(资源的转移)
缺陷:多个对象不能同时访问同一份资源
namespace bia
{
	template <class T>
	class auto_ptr
	{
	public:
		//RAII
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		//ap2(ap1);
		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		//ap1=ap2
		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				if (_ptr)
					delete _ptr;

				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}

			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		//让类对象具有指针类似的行为
		T& operator*()
		{
			return *_ptr;
		}

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

	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	//常规指针
	int a = 10;
	int* p1 = &a;
	int* p2(p1);

	*p1 = 20;
	*p2 = 30;

	bia::auto_ptr<int> ap1(new int);

	//解决方式:将资源转移
	bia::auto_ptr<int> ap2(ap1);
	//*ap1 = 10;//会崩溃---ap1已经将资源转移给了ap2,自己与资源断开链接
	*ap2 = 20;

	bia::auto_ptr<int> ap3(new int);
	ap3 = ap2;
}
auto_ptr(2.0)
RAII+类似指针的行为+解决浅拷贝(资源管理权的转移)
缺陷:可能会产生野指针
namespace bia
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr = nullptr)
			:_ptr(_ptr)
			,_owner(false)
		{
			if (_ptr)
				_owner = true;
		}

		auto_ptr(const auto_ptr<T>& ap)
			: _ptr(ap._ptr)
			, _owner(ap._owner)
		{
			ap._owner = false;
		}

		auto_ptr<T>& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				//先处理以前的旧资源
				if (_ptr&&_owner)
					delete _ptr;

				_ptr = ap._ptr;
				_owner = ap._owner;
				ap._owner = false;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr&&_owner)
				delete _ptr;
		}

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

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

	protected:
		T* _ptr;
		//mutable  可变的,与const恰好相反
		mutable bool _owner;//资源真正的拥有者(对资源有释放权力的对象)
	};
}
void TestFunc()
{
	bia::auto_ptr<int> ap1(new int);
	bia::auto_ptr<int> ap2(ap1);
	bia::auto_ptr<int> ap3;
	ap3 = ap2;
}

此时,ap1和ap2都已经是野指针了。

auto_ptr(3.0)
恢复到版本1.0+其他内容

建议:什么情况下都不要使用auto_ptr

C++11:
unique_ptr

RAII+类似指针的行为+对象独占资源(防止被拷贝)
--->C++98:拷贝构造&&赋值运算符重载  (只声明不定义+访问限制private)
--->C++11:拷贝构造&&赋值运算符重载两个函数后跟=delete
缺陷:对象之间不能共享资源

//private:C++98  只声明不定义+private
		unique_ptr(const unique_ptr<T>& ap);
		unique_ptr<T>& operator=(const unique_ptr<T>& ap);
//C++11
		unique_ptr(const unique_ptr<T>& ap) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& ap) = delete;

shared_ptr

RAII+类似指针行为+引用计数(记录资源被对象共享的次数)
缺陷:1.不是线程安全   2.只能管理new的资源   3.存在循环引用的问题
解决:1.加入mutex来保证计数的安全性
        mutex:可以保证计数的安全性||可以保证shared_ptr类本身
              但不能保证shared_ptr管理资源内容的安全性
      2.定制删除器------仿函数       例如:文件指针的关闭,malloc需要free掉
      3.加入weak_ptr(weak_ptr是) 
//定制删除器
template <class T>
class DelRef
{
public:
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			delete ptr;
			ptr = nullptr;
		}
	}
};

template <class T>
class Free
{
public:
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			free(ptr);
			ptr = nullptr;
		}
	}
};

class FClose
{
public:
	void operator()(FILE*& pf)
	{
		if (pf)
		{
			fclose(pf);
			pf = nullptr;
		}
	}
};

namespace bia
{
	template <class T,class DF= DelRef<T>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pcount(nullptr)
			, _pmutex(nullptr)
		{
			if (_ptr)
			{
				_pcount = new int(1);
				_pmutex = new mutex;
			}
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
			, _pmutex(sp._pmutex)
		{
			if (_pcount)
			{
				AddRef();
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				//让当前对象与资源断开
				Release();

				//与sp共享
				_ptr = sp._ptr;
				_pcount = sp._pcount;
				if (_pcount)
				{
					AddRef();
				}
				return *this;
			}
		}

			~shared_ptr()
			{
				Release();
			}

			//具有指针行为
			T& operator*()
			{
				return *_ptr;
			}

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

			int use_count()
			{
				return *_pcount;
			}
	private:
		void AddRef()
		{
			_pmutex->lock();
			++(*_pcount);
			_pmutex->unlock();
		}

		int SubRef()
		{
			_pmutex->lock();
			--(*_pcount);
			_pmutex->unlock();

			return *_pcount;
		}

		void Release()
		{
			if (_ptr && 0 == SubRef())
			{
				DF d;
				d(_ptr);
				delete _pcount;
			}
		}

	protected:
		T* _ptr;
		int* _pcount;
		mutex* _pmutex;
	};
}

void TestSharedPtr()
{
	bia::shared_ptr<int> sp1(new int);
	bia::shared_ptr<int, Free<int>> sp2((int*)malloc(sizeof(int)));
	bia::shared_ptr<FILE, FClose> sp3(fopen("1.txt", "rb"));
}

以上解决了前两个问题,第三个循环引用的问题
例如:双向链表中的next与pre
在这里插入图片描述
互相制约,都在等对方销毁,从而造成死锁

weak_ptr:RAII+类似指针行为+引用计数
作用:配合shared_ptr使用,解决循环引用问题
注意:weak_ptr不能单独管理资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值