【C/C++ 14】C++11智能指针

目录

一、智能指针概述

二、auto_ptr

三、unique_ptr

四、shared_ptr

五、weak_ptr

六、定制删除器


一、智能指针概述

C++在进行异常处理的时候,若在new和delete之间或在lock和unlock之间就抛出异常了,这样会导致内存泄漏或死锁问题。

为了解决上述问题,于是就引入了智能指针(RAII)的概念。

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

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。

#include 
#include 
using namespace std;

// 使用RAII实现的SmartPtr
template
class SmartPtr
{
public:
	SmartPtr(T* ptr = nullptr)
		: _ptr(ptr)
	{}

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

	// 重载 * 和 ->
	T& operator*()
	{
		return *_ptr;
	}

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

	// 重载指针的 + ++ -- 功能,自增自减区分前置和后置
	T* operator+(int i)
	{
		T* cur = _ptr;
		while (i--)
		{
			cur++;
		}
		return cur;
	}

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

	T* operator++(int)
	{
		T* cur = _ptr;
		++_ptr;
		return cur;
	}

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

	T* operator--(int)
	{
		T* cur = _ptr;
		--_ptr;
		return cur;
	}

private:
	T* _ptr;
};

double Div(int a, int b)
{
	if (b == 0)
		throw invalid_argument("除0错误");
	return (double)a / (double)b;
}

void Func()
{
	SmartPtr sp1(new int);
	SmartPtr sp2(new int[10]);

	*sp1 = 5;
	cout << "*sp1 = " << *sp1 << endl;

	for (int i = 0; i < 10; ++i)
	{
		*(sp2 + i) = i;
		printf("sp2[%d] = %d\n", i, *(sp2 + i));
	}

	cout << Div(*sp1, *sp2) << endl;
}

int main()
{
	try 
	{
		Func();
	}
	catch (const exception& e) 
	{
		cout << e.what() << endl;
	}

	return 0;
}

二、auto_ptr

auto_ptr是C++98提出的智能指针,核心思想是转移管理,但是因其可能引发指针悬空的问题,故很多企业都禁止使用auto_ptr。

#include 
using namespace std;

namespace my
{
	template
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

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

		auto_ptr& operator=(auto_ptr& ap)
		{
			// 检测是否为自己给自己赋值
			if (this != &ap)
			{
				// 释放当前对象中资源
				if (_ptr)
					delete _ptr;
				// 转移ap中资源到当前对象中
				_ptr = ap._ptr;
				ap._ptr = NULL;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}

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

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

	private:
		T* _ptr;
	};
}

// 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
int main()
{
	my::auto_ptr sp1(new int);
	my::auto_ptr sp2(sp1); // 管理权转移

	// sp1悬空
	*sp2 = 10;
	cout << *sp2 << endl;
	cout << *sp1 << endl;	// 对空指针解引用
	return 0;
}

三、unique_ptr

unique_ptr的实现原理:单例模式,简单粗暴的防拷贝。

// C++11中开始提供更靠谱的智能指针:unique_ptr / shared_ptr / weak_ptr

#include 
#include 

namespace my
{
	template
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			: _ptr(ptr)
		{}

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

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

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

		// 进制拷贝,单例模式
		unique_ptr(const unique_ptr& sp) = delete;
		unique_ptr& operator=(const unique_ptr& sp) = delete;

	private:
		T* _ptr;
	};
}

四、shared_ptr

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

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

namespace my
{
	template
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr), _pRefCount(new int(1)), _pmtx(new std::mutex)
		{}

		shared_ptr(const my::shared_ptr& sp)
			: _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
		{
			AddRef();
		}

		my::shared_ptr& operator=(const my::shared_ptr& sp)
		{
			if (_ptr != sp._ptr)
			{
				Release();
				_ptr = sp._ptr;
				_pRefCount = sp._pRefCount;
				_pmtx = sp._pmtx;
				AddRef();
			}
			return *this;
		}

		int use_count()
		{
			return *_pRefCount;
		}

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

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

		T* get()const
		{
			return _ptr;
		}

		~shared_ptr()
		{
			Release();
		}

	private:
		void AddRef()
		{
			_pmtx->lock();
			*_pRefCount += 1;
			_pmtx->unlock();
		}

		void Release()
		{
			_pmtx->lock();
			bool flag = false;
			if (--(*_pRefCount) == 0 && _ptr)
			{
				delete _ptr;
				delete _pRefCount;
				flag = true;
			}
			_pmtx->unlock();

			if (flag == true)
				delete _pmtx;
		}

	private:
		T* _ptr;
		int* _pRefCount;
		std::mutex* _pmtx;
	};
}

五、weak_ptr

weak_ptr称为弱指针,是一种配合shared_ptr而引入的一种智能指针,为了解决shared_ptr引用成环问题而存在。
weak_ptr指向一个由shared_ptr管理的对象而不影响所指对象的生命周期(不改变所指对象的引用计数)。
weak_ptr不提供 operator* 和 operator-> 的重载,不可直接通过weak_ptr使用对象。
weak_ptr可以指向资源,但不参与资源的管理。

#include 
#include 

namespace my
{
	template
	class weak_ptr
	{
	public:
		weak_ptr(T* ptr = nullptr)
			: _ptr(ptr)
		{}

		weak_ptr(const my::shared_ptr& sp)
			: _ptr(sp.get())
		{}

		weak_ptr& operator=(const shared_ptr& sp)
		{
			_ptr = sp.get();
			return *this;
		}



	private:
		T* _ptr;
	};
}

六、定制删除器

#define _CRT_SECURE_NO_WARNINGS 1

// 上述简单实现的 unique_ptr / shared_ptr / weak_ptr 是存在缺陷的
// 一个最大的缺陷就是释放资源只能是默认的 delete 处理
// 所以我们需要定制删除器,可以通过仿函数或者lambda实现

#include 

// 定制删除器
template
struct DeleteArray
{
	void operator()(const T* ptr)
	{
		delete[] ptr;
	}
};

int main()
{
	std::shared_ptr sp1(new int[10], DeleteArray());
	std::shared_ptr sp2(new std::string[10], DeleteArray());

	std::shared_ptr sp3(fopen("Test.cpp", "w"), [](FILE* ptr) {fclose(ptr); });
}

你可能感兴趣的:(C/C++,c++,开发语言)