C++----智能指针

1、为什么需要智能指针

#include <iostream>
using namespace std;

// C/C++
bool Test1()
{
	// 成功返回true,失败返回false
	return false;
}

void Test2()
{
	// ..

	// 假设此处遇到非法情况
	throw 1;
}


// 如果采用原生态指针(T*)管理资源,程序存在资源泄漏的风险就比较大

void TestFunc()
{
	int* p = new int[10];
	FILE* pf = fopen("2222.txt", "rb");
	if (nullptr == pf)
	{
		delete[] p;
		return;
	}

	// ...
	if (!Test1())
	{
		delete[] p;
		fclose(pf);
		return;
	}

	try
	{
		Test2();
	}
	catch (...)
	{
		delete[] p;
		fclose(pf);
		throw;
	}

	// ...

	delete[] p;
	fclose(pf);
}

问题分析:上面的问题分析出来我们发现有以下两个问题?
1.存在内存泄漏的问题。
2. 异常安全问题。
我们在编写程序时必需要时刻注意这俩个问题,稍不留心,便会造成内存泄漏。

那能否让程序自我感知:对象在销毁时自动释放资源?

2、智能指针的使用及原理

C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr。虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr。shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针。

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象.

在构造函数中放资源
在析构函数中释放资源

这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效

3.1 智能指针的原理

template<class T>

class SmartPtr
{
public:
	// RAII: 作用--->用户不用考虑什么是该释放资源
	//              把释放资源的实际交给编译器

	SmartPtr(T* ptr = nullptr)
		:_ptr(ptr)
	{
	}

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

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

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

struct A
{
	int a;
	int b;
	int c;
};

void TestSmartPtr()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 10;

	SmartPtr<A> sp2(new A);
	sp2->a = 10;
	sp2->b = 20;
	sp2->c = 30;
	//SmartPtr<A> sp3(new A); // 浅拷贝
}

/缺陷: 浅拷贝

c++98 ---- auto_ptr
解决浅拷贝方式:资源转移—>当前对象
即总是让最新的指针指向资源,原指针与资源断开联系

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

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

	auto_ptr(auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;  // 资源转移
	}

	auto_ptr<T> operator=(const auto_ptr<T>& ap)
	{
		if (this != &ap)
		{
			if (_ptr)
			{
				delete _ptr;
			}
			_ptr = ap._ptr;
			ap._ptr = nullptr;
		}
		return *this;
	}
private:
	T* _ptr;
};


void TestAutoPtr()
{
	auto_ptr<int> ap1(new int);
	auto_ptr<int> ap2(ap1);
	*ap1 = 10; 会产生新的问题 , ap1 无法在使用

}

产生新问题, 所以建议不要使用auto_ptr;

C++11:提供更靠谱的智能指针unique_ptr , 对 auto_ptr—>保留:资源转移实现的

unique_ptr 智能指针原理:

		RAII(自动释放资源) + operator*()/operator->()(具有指针类似行为) + 提供解决浅拷贝方式

浅拷贝引起原因:默认拷贝构造函数 和 默认的赋值运算符重载
unique_ptr ----> 不让拷贝和赋值

template<class Tclass DF = DFDef<T>>
	class unique_ptr
	{
	public:
		// RAII
		unique_ptr(T* ptr = nullptr)
			: _ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr; // 释放资源的方式固定死了,只能管理new的资源,不能处理任意类型的资源
				_ptr = nullptr;
			}
		}

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

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

		// 解决浅拷贝:禁止调用拷贝构造函数和赋值运算符重载

		// C++98:
		/*推荐
	private:  // 拷贝构造的权限一定要设置成private,原因:为了防止用户自己在类外定义
		unique_ptr(const unique_ptr<T>& up);
		unique_ptr<T>& operator=(const unique_ptr<T>&);
		*/

		// C++11 禁止调用拷贝构造和赋值运算符重载

		// 1. 释放new的空间  2.默认成员函数 = delete : 告诉编译器,删除该默认成员函数
		unique_ptr(const unique_ptr<T>&) = delete;  
			
		unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;
	private:
		T* _ptr;
	};

	// 用户在外部实现
// 	template<class T>
// 	unique_ptr<T>::unique_ptr(const unique_ptr<T>& up)
// 	{}


void TestUniquePtr()
{
	unique_ptr<int> up1(new int);
	//unique_ptr<int> up2(up1);

	unique_ptr<int> up3(new int);
	//up3 = up1;
}

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了
template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(nullptr)
		{
			if (_ptr)  //若 _ptr 不为空
				_pCount = new int(1);
		}

		~shared_ptr()
		{
		 // 若有资源 且 当前对象为最后一个使用资源的对象 ,可释放资源
			if (_ptr && 0 == --*_pCount)
			{
				delete _ptr;
				delete _pCount;
			}
		}

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

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

		shared_ptr(const shared_ptr<T>& sp)
			: _ptr(sp._ptr) // 共享同一份资源
			, _pCount(sp._pCount) // 共享同一份计数
		{
			if (_ptr)// 必须保证 _ptr 是有资源的 _ptr != nullptr
				++*_pCount;
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				// 1. 与旧资源断开联系
				// this现在不是用自己的资源,要与sp对象共享资源
				if (_ptr && 0 == --*_pCount)
				{
					// 当前对象是最后一个使用资源的对象
					delete _ptr;
					delete _pCount;
				}

				// 2. 与sp共享资源及计数
				_ptr = sp._ptr;
				_pCount = sp._pCount;

				if (_ptr)//判断是否有资源
					++*_pCount;

			}

			return *this;
		}

		int use_count()
		{
			return *_pCount;
		}

	private:
		T* _ptr;
		int* _pCount;
	};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值