当前位置: 首页 > article >正文

【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 <iostream>
#include <map>
using namespace std;

// 使用RAII实现的SmartPtr
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;
	}

	// 重载指针的 + ++ -- 功能,自增自减区分前置和后置
	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<int> sp1(new int);
	SmartPtr<int> 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 <iostream>
using namespace std;

namespace my
{
	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<T>& operator=(auto_ptr<T>& 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<int> sp1(new int);
	my::auto_ptr<int> 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 <iostream>
#include <mutex>

namespace my
{
	template<class T>
	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<T>& sp) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;

	private:
		T* _ptr;
	};
}

四、shared_ptr

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

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

namespace my
{
	template<class T>
	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<T>& sp)
			: _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
		{
			AddRef();
		}

		my::shared_ptr<T>& operator=(const my::shared_ptr<T>& 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 <iostream>
#include <mutex>

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

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

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



	private:
		T* _ptr;
	};
}

六、定制删除器

#define _CRT_SECURE_NO_WARNINGS 1

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

#include <iostream>

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

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

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


http://www.kler.cn/a/228206.html

相关文章:

  • Linux 内核学习 3b - 和copilot 讨论pci设备的物理地址在内核空间和用户空间映射到虚拟地址的区别
  • 【STM32HAL-----GPIO】
  • 安卓14自由窗口圆角处理之绘制圆角轮廓线
  • 电子应用设计方案103:智能家庭AI浴缸系统设计
  • 由于请求的竞态问题,前端仔喜提了一个bug
  • 智能化加速标准和协议的更新并推动验证IP(VIP)在芯片设计中的更广泛应用
  • MySQL操作问题汇总
  • 06 - python操作xml
  • CI/CD总结
  • 【算法题】91. 解码方法
  • 详解 Spring Boot 条件装配
  • 考研中常见的算法-逆置
  • 在 iOS 上安装自定企业级应用
  • Matplotlib绘制炫酷柱状图的艺术与技巧【第60篇—python:Matplotlib绘制柱状图】
  • 2 月 3 日算法练习-数论
  • 网络安全笔记
  • 假期刷题打卡--Day23
  • 蓝桥杯Web应用开发-display属性
  • 开源计算机视觉库OpenCV详细介绍
  • Ainx框架实现 一
  • spring boot3x登录开发-上(整合jwt)
  • Bagging的随机森林;Boosting的AdaBoost和GBDT
  • 【Kotlin】Kotlin环境搭建
  • 【用Unity开发一款横板跳跃游戏部分需要学习的技术点指南】
  • Python基础学习 -05-2 基本类型
  • 【蓝桥杯冲冲冲】[NOIP2001 普及组] 装箱问题