C++RAII内存管理技术

这篇具有很好参考价值的文章主要介绍了C++RAII内存管理技术。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++
文章来源地址https://www.toymoban.com/news/detail-650948.html

一.什么是RAII内存管理技术?

  • C++在引入异常机制后,代码执行流的跳转变得难以预料,如果使用普通的指针进行内存管理,很难避免内存泄漏的问题(执行流跳转导致堆区资源无法被释放)
  • RAII技术指的是利用对象的生命周期来管理内存资源,就堆区内存资源的管理而言,指的就是:将指针封装在类中,在类对象构造时获取堆区资源,当类对象生命周期结束时,通过类对象的析构函数自动完成堆区资源的释放,这样的类对象就是智能指针
    • 智能指针可以有效地避免开发中出现内存泄漏的问题,同时为开发者省去了很多时间和精力

二.智能指针

  • C++11标准库中智能指针主要分为三大类:C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++

unique_ptr

  • unique_ptr对象之间不允许进行拷贝,即一个unique_ptr对象管理一块堆区内存资源,是一一对应的关系
  • unique_ptr的简单实现原理:
	//不允许拷贝的智能指针
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	template<class T,class delfunc>
	class unique_ptr
	{
	public:
		unique_ptr(T * ptr = nullptr)
			:_ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				delfunc del;
				del(_ptr);
				std::cout << "delete" << std::endl;
			}
		}


		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

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

		//禁止unique_ptr对象间的拷贝
		unique_ptr(const unique_ptr<T, delfunc>& unptr) = delete;
		unique_ptr<T, delfunc>& operator=(const unique_ptr<T, delfunc>& unptr) = delete;
	private:
		T* _ptr;
	};
  • 使用指针管理堆区资源时,我们会让其指向单个对象或者对象数组,单个对象delete完成资源释放,而对象数组要采用delete[]完成资源释放.unique_ptr的类模板参数delfunc的设计目的就是为了区分上述两种情况,让使用者通过设计仿函数的方式在deletedelete[]之间做选择

shared_ptr

  • shared_ptr是允许进行拷贝的智能指针,然而shared_ptr对象之间发生拷贝时,就会出现多个智能指针对象同时管理同一块堆区内存的情况,shared_ptr通过引用计数的技术避免了同一块堆区资源被释放多次的情况出现:C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++
  • 引用计数的实现思路:
    • shared_ptr内部封装一个指针int * recount用来维护引用计数变量
    • 每当一个shared_ptr对象进行构造并申请堆区资源时,同时在堆区申请一个int变量作为该shared_ptr对象所指向的堆区资源的引用计数变量
    • 每当shared_ptr被拷贝时,连同int * recount一起拷贝,然后让引用计数变量加一即可(赋值重载的实现还要考虑引用计数变量减一和堆区资源释放的问题)
    • 每当shared_ptr析构时,引用计数变量减一,若引用计数变量减为0,则释放堆区资源C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++
  • shared_ptr的简单实现原理:
    //允许拷贝的智能指针,利用引用计数解决内存管理冲突
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	template<class T, class delfunc>
	class shared_ptr
	{
	public:
		//构造智能指针(同时创建引用计数变量)
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr),
			 _recount(new int(1))
		{}
		shared_ptr(const shared_ptr<T, delfunc>& shptr)
			: _ptr(shptr._ptr),
			  _recount(shptr._recount)
		{
			//智能指针拷贝增加引用计数
			(*_recount)++;
		}
		shared_ptr<T, delfunc>& operator=(const shared_ptr<T, delfunc>& shptr)
		{
			if (_ptr != shptr._ptr)
			{
				//智能指针指向改变,其先前指向的堆区内存和引用计数变量需要处理
				Release();
				//完成智能指针拷贝并增加引用计数
				_ptr = shptr._ptr;
				_recount = shptr._recount;
				++(*_recount);
			}
			return (*this);
		}
		~shared_ptr()
		{
			Release();
		}


		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
		T* getptr() const
		{
			return _ptr;
		}
	private:
		//将资源释放函数进行封装,方便复用
		//引用计数减一,若引用计数减为0则释放资源
		void Release()
		{
			//引用计数减一
			--(*_recount);
			//引用计数为0则释放资源
			if (*_recount == 0)
			{
				if (_ptr)
				{
					delfunc del;
					del(_ptr);
					std::cout << "delete" << std::endl;
				}
				//同时注意释放引用计数变量
				delete _recount;
			}
		}
	private:
		T* _ptr;
		int* _recount;
	};
  • shared_ptr类内部同时还要解决引用计数变量带来的线程安全的问题,这里暂不讨论

循环引用问题

  • shared_ptr用在自引用结构体中会出现对象无法析构的问题:

template<class T>
class deletefunc
{
public:
	void operator()(T* ptr)
	{
		delete  ptr;
	}
};
// 循环引用
struct ListNode
{
	int _val;
	shared_ptr<ListNode,deletefunc<ListNode>> _next;
	shared_ptr<ListNode,deletefunc<ListNode>> _prev;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

// 循环引用
void test_shared_cycle()
{
	share_ptr<ListNode, deletefunc<ListNode>> n1(new ListNode);
	share_ptr<ListNode, deletefunc<ListNode>> n2(new ListNode);
	n1->_next = n2;
	n2->_prev = n1;
}
  • test_shared_cycle()函数执行完后,两个链表节点都无法正常析构,shared_ptr引发了循环引用的问题:C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++
  • n1节点的析构依赖于n2节点中_prev指针的析构,n2节点中_prev指针的析构依赖于n2节点的析构,n2节点的析构又依赖于n1节点中_next指针的析构, n1节点中_next指针的析构又依赖于n1节点的析构.如此往复构成了循环的依赖关系导致两个节点都无法被释放.
  • weak_ptr就是为了解决循环引用问题而设计的

weak_ptr

  • weak_ptr智能指针不参与堆区内存的申请和释放,也不会增加特定内存块的引用计数(其功能和普通指针差不多),weak_ptr支持以shared_ptr为引用形参的拷贝构造和赋值
  • weak_ptr简单实现原理:
	//用于配合share_ptr的使用用于循环引用的场景
	//delfunc是堆区资源释放函数,用于调用delete或者delete[]
	//weak_ptr不参与资源的申请和释放
	template<class T, class delfunc>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		~weak_ptr()
		{}
		
		weak_ptr(const weak_ptr<T, delfunc>& wptr)
			:_ptr(wptr._ptr)
		{}
		weak_ptr(const share_ptr<T, delfunc>& shptr)
			:_ptr(shptr.getptr())
		{}
		weak_ptr<T, delfunc>& operator=(const weak_ptr<T, delfunc>& wptr)
		{
			_ptr = wptr._ptr;
			return (*this);
		}
		weak_ptr<T, delfunc>& operator=(const share_ptr<T, delfunc>& shptr)
		{
			_ptr = shptr.getptr();
			return (*this);
		}

		//让智能指针可以像指针一样被使用
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
  • 自引用结构体中采用weak_ptr就可以避免出现循环引用的问题
    C++RAII内存管理技术,进阶C++(C++与进阶数据结构),c++

到了这里,关于C++RAII内存管理技术的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言】动态内存管理(C语言的难点与精华,数据结构的前置知识,你真的掌握了吗?)

    学习专栏 : 《零基础学C语言》 《数据结构世界》 俗话说的好,要想学好 数据结构 (数据结构世界,对数据结构感兴趣的小伙伴可以移步),就必须学好以下三方面知识: 指针 不允许你还不了解指针的那些事(一)(内存和地址+指针变量+指针运算+野指针+传址调用) 不

    2024年02月05日
    浏览(33)
  • 数据结构专题实验7——图的应用(景点管理)(C++实现)

    实验内容:应用图的技术,根据需求文件要求的功能,实现旅游景点的管理。实验要求: 使用图的数据结构建立一个景点图的结构。 可以查找各景点信息。 旅游景点导航,使用深度优先,从一个景点开始遍历所有景点。 查找一个景点到另一个景点的最短路径。 对景点道路

    2024年02月04日
    浏览(35)
  • 数据结构:动态内存分配+内存分区+宏+结构体

    1.定义一个学生结构体,包含结构体成员:身高,姓名,成绩;定义一个结构体数组有7个成员,要求终端输入结构体成员的值,根据学生成绩,进行冒泡排序。 1.申请一个10个int类型的堆区空间,并实现选择排序(需要导入头文件 #include stdlib.h) 2.用##拼接带参宏的参数 3.宏函

    2024年02月20日
    浏览(34)
  • Java进阶篇--数据结构

    #我的编程语言学习笔记# 目录 一.数组(Array): 1.1  特点: 1.2  基本操作: 1.3  使用数组的好处包括: 1.4  数组也有一些限制: 二.集合框架(Collections Framework): 2.1  列表(List): 2.1.1  数组列表(ArrayList): 扩展知识: 方法: ArrayList的遍历方式 ArrayList使用泛型 A

    2024年02月12日
    浏览(25)
  • 数据结构进阶(一):AVL树

    所谓的AVL树也叫做高度平衡的二叉搜索树。 啥是高度平衡的二叉搜索树? 高度平衡的二叉搜索树: 意味着左右子树的高度最大不超过一 。 我们先来回顾一下二叉搜索树的概念: 二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树: 若它的左子树

    2024年02月12日
    浏览(28)
  • 【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,结构体的基本使用和常见错误在上一篇详细讲解过了,不知道大家都学会了没有。    ⛳️ 今天给大家来个硬菜,教点高

    2024年02月15日
    浏览(36)
  • 十三、集合进阶——单列集合 及 数据结构

    List系列集合 :添加的元素是有序、可重复、有索引。 Set系列集合 :添加的元素是无序、不重复、无索引。 1. Collection Collection 是单列集合的 祖宗 接口,它的功能是 全部单列集合都可以继承使用的。 Contains方法 细节 : 2.Collection 的遍历方式 迭代器遍历 迭代器 不依赖索引

    2024年02月21日
    浏览(32)
  • 数据结构_进阶(1):搜索二叉树

    建议再看这节之前能对C++有一定了解 二叉树在前面C的数据结构阶段时有出过,现在我们对二叉树来学习一些更复杂的类型,也为之后C++学习的 map 和 set 做铺垫 1. map和set特性需要先铺垫二叉搜索树 ,而二叉搜索树也是一种树形结构 2. 二叉搜索树的特性了解,有助于更好的理

    2024年02月16日
    浏览(40)
  • Linux中与“系统内存”相关的内核数据结构

    【摘要】本文讲述了在Linux中与系统内存相关的内核数据结构,通过逐行分析源代码讲述了内存数据结构之间的关联。 7.1 mm_struct 指向进程所拥有的内存描述符,保存了进程的内存管理信息 7.2 vm_area_struct 进程虚拟内存的每个区域表示为 struct vm_area_struct 的一个实例。 vm_flags是

    2024年02月04日
    浏览(29)
  • 【算法数据结构系列】哈夫曼树进阶解读

    作者:半身风雪 简介:移动开发全栈领域工作者

    2024年02月10日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包