智能指针(RAII)

news2024/9/24 11:27:13

智能指针(RAII)

  • 一、内存泄漏
    • 1、介绍
    • 2、原因
    • 3、泄漏的内存类型分类
  • 二、RAII
    • 1、介绍
    • 2、基本思想
    • 3、优点
    • 4、实现方式
  • 三、unique_ptr
    • 1、介绍
    • 2、主要特性
    • 3、注意事项
    • 4、unique_ptr类
    • 5、示例代码
    • 6、运行结果
    • 7、简单实现
  • 四、shared_ptr
    • 1、介绍
    • 2、主要特点
    • 3、注意事项
    • 4、shared_ptr类
    • 5、简单实现
  • 五、weak_ptr
    • 1、介绍
    • 2、weak_ptr类
    • 3、shared_ptr的循环引用问题
    • 4、循环引用示例代码

一、内存泄漏

1、介绍

  • 内存泄漏(Memory Leak)是计算机科学中的一个常见问题,指的是程序在运行过程中,未能释放那些已经不再使用的内存空间。随着时间的推移,这些未释放的内存会逐渐累积,最终导致程序可用的内存越来越少,可能引发程序运行缓慢、崩溃或者系统资源耗尽等问题。

2、原因

  • 申请内存成功,相关工作结束后不释放这些内存,这是最常见的内存泄漏原因。
  • 错误的内存管理逻辑,如重复释放内存(double free)、未检查内存是否分配成功就使用等等。
  • 缓存策略不当,在某些情况下,程序会缓存数据以提高性能,但如果缓存策略不好,如没有适当的清理机制,就可能导致内存泄漏。
  • 使用的第三方库或框架可能存在内存泄漏的问题。
  • 作用域问题,如果局部变量(如动态分配的对象)的作用域过大,且未在使用完毕后及时释放,可能导致内存泄漏。

3、泄漏的内存类型分类

  • 堆内存泄漏:堆内存指的是程序执行过程中通过malloc、calloc、realloc(C语言)或new(C++语言)等函数从堆上分配的内存。如果这部分内存没有被正确地释放(如忘记调用free或delete),就会导致堆内存泄漏。
  • 系统资源泄漏:程序使用系统分配的其他资源,如文件描述符、套接字、数据库连接等等。如果这些资源在使用完毕后没有被正确地释放,如忘记关闭文件、断开网络连接等等,就会导致系统资源泄漏。

二、RAII

1、介绍

  • RAII(Resource Acquisition Is Initialization)是一种在C++编程中广泛使用的资源管理技术。它基于C++的构造函数和析构函数的调用机制,确保资源(动态分配的内存、文件句柄、网络连接等等)在对象生命周期内被正确地获取和释放。

2、基本思想

  • 资源的获取(Acquisition) 与对象的初始化(Initialization) 绑定在一起。这意味着,当对象被创建时,它会自动获取所需的资源。
  • 资源的释放(Release) 与对象的析构(Destruction) 绑定在一起。这意味着,当对象的生命周期结束时,例如,对象被销毁或作用域结束,它的析构函数会自动释放它所管理的资源。

3、优点

  • 自动管理资源:RAII通过自动调用构造函数和析构函数来管理资源,减少了手动管理资源的复杂性和出错的可能性。
  • 异常安全:在C++中,异常可能会导致函数提前退出。使用RAII,即使发生异常,对象的析构函数也会被调用,从而确保资源被正确地释放。
  • 减少内存泄漏:由于资源在对象析构时自动释放,因此可以显著减少内存泄漏的风险。

4、实现方式

  • RAII通常通过封装资源的类来实现。这些类通常包含以下部分。
  • 构造函数:在构造函数中,类会获取所需的资源。
  • 析构函数:在析构函数中,类会释放它所管理的资源。
  • 拷贝构造函数和赋值运算符重载(可选,但通常需要禁用或适当实现,以防止资源被错误地复制或赋值):如果对象包含独占资源(如文件句柄),则应该禁用拷贝构造函数和赋值运算符重载,或者实现深拷贝以确保资源被正确管理。
  • 关于对应函数的介绍参见类与对象(中)。

三、unique_ptr

1、介绍

  • unique_ptr 是C++11引入的一个智能指针,用于管理动态分配的内存,确保资源(如动态分配的对象、数组等)在不再需要时能够被自动释放。
  • 与shared_ptr不同,unique_ptr拥有其所指对象的唯一所有权,即两个unique_ptr不能同时指向同一个对象。

2、主要特性

  • 唯一所有权:unique_ptr不允许复制(copy),但支持移动(move),这保证了在任何时候只有一个unique_ptr实例拥有对资源的所有权。
  • 自动释放:当unique_ptr的实例被销毁时(例如,离开作用域时),它会自动调用其指向对象的析构函数(如果是对象的话),并释放分配的内存。
  • 自定义删除器:unique_ptr允许指定一个自定义的删除器,该删除器将在unique_ptr被销毁时调用,以执行资源的释放逻辑。
  • 数组支持:unique_ptr有两个特化版本,一个用于单个对象,另一个用于对象数组。对于数组版本,unique_ptr会调用delete[]而不是delete来释放内存。

3、注意事项

  • 由于unique_ptr不支持复制,因此不能直接将一个unique_ptr赋值给另一个同类型的unique_ptr,否则会导致编译错误。但是,可以使用move来转移所有权。
  • 当使用unique_ptr管理动态分配的数组时,需确保使用unique_ptr<T[]>的特化版本,以便正确调用delete[]而不是delete。
  • unique_ptr是轻量级的,并且通常比shared_ptr有更好的性能,因为它不涉及额外的开销(如控制块)来管理共享所有权。然而,这也意味着它不能用于需要在多个所有者之间共享资源的场景。

4、unique_ptr类

在这里插入图片描述

5、示例代码

class Test_unique_ptr_Class {
public:
	Test_unique_ptr_Class() { std::cout << "MyClass created\n"; }
	~Test_unique_ptr_Class() { std::cout << "MyClass destroyed\n"; }
	void Func() { std::cout << "Doing something...\n"; }
};

void Test_unique_ptr1()
{
	std::unique_ptr<Test_unique_ptr_Class> ptr = std::make_unique<Test_unique_ptr_Class>();
	ptr->Func();
}

void Test_unique_ptr2()
{
	std::unique_ptr<int[]> arr = std::make_unique<int[]>(4);
	for (int i = 0; i < 4; ++i) {
		arr[i] = i * i;
	}
}

int main()
{
	Test_unique_ptr1();
	Test_unique_ptr2();

	return 0;
}

6、运行结果

在这里插入图片描述

7、简单实现

template<class T>
class unique_ptr
{
public:
	unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	~unique_ptr()
	{
		if (_ptr)
		{
			cout << "~unique_ptr()" << endl;
			delete _ptr;
			_ptr = nullptr;
		}
	}

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

	T* operator->()
	{
		return _ptr;
	}
	
	unique_ptr(const unique_ptr<T>& up) = delete;
	unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
	
private:
	T* _ptr;
};

四、shared_ptr

1、介绍

  • shared_ptr是C++标准库中的一个智能指针模板类,用于自动管理具有共享所有权的动态分配的对象。通过使用shared_ptr可以避免手动管理内存(如使用new和delete),从而减少内存泄漏的风险。

2、主要特点

  • 共享所有权:多个shared_ptr实例可以指向同一个对象。当最后一个指向该对象的shared_ptr被销毁或重置时,对象会被自动删除。
  • 引用计数:shared_ptr使用一个内部计数器来跟踪有多少个shared_ptr实例指向某个对象。每当一个新的shared_ptr被创建并指向该对象时,计数器递增;每当一个shared_ptr被销毁或重置时,计数器递减。当计数器达到零时,对象被删除。
  • 线程安全:shared_ptr的引用计数操作是线程安全的,允许多个线程安全地共享对象。但是,这并不意味着指向的对象本身是线程安全的;如果多个线程需要修改对象的状态,则需要额外的同步机制。

3、注意事项

  • 性能开销:虽然shared_ptr提供了方便的内存管理,但它也引入了额外的性能开销(如引用计数的维护)。在性能敏感的应用中,需要权衡使用shared_ptr的利弊。
  • 自定义删除器:shared_ptr允许指定一个自定义的删除器,用于在对象被销毁时执行特定的清理操作。这可以通过传递一个删除器给shared_ptr的构造函数来实现。

4、shared_ptr类

在这里插入图片描述
在这里插入图片描述

5、简单实现

template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pcount(new atomic<int>(1))
	{}

	template<class D>
	shared_ptr(T* ptr, D del)
		:_ptr(ptr)
		,_del(del)
		, _pcount(new atomic<int>(1))
	{}

	void Release()
	{
		if (--(*_pcount) == 0)
		{
			_del(_ptr);
			delete _pcount;
			_ptr = nullptr;
			_pcount = nullptr;
		}
	}

	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		,_pcount(sp._pcount)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			Release();
			_ptr = sp._ptr;
			_pcount = sp._pcount;
			++(*_pcount);
		}
		return *this;
	}

	~shared_ptr()
	{
		Release();
	}

	T* GetPtr() const
	{
		return _ptr;
	}

	int GetUseCount()	const
	{
		return *_pcount;
	}

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

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

private:
	T* _ptr;
	atomic<int>* _pcount;
	function<void(T*)> _del = [](T* ptr) {delete ptr; };
};

五、weak_ptr

1、介绍

  • weak_ptr是C++11引入的一种智能指针,它用于解决shared_ptr之间的循环引用问题。当两个或多个shared_ptr相互引用时,就会形成一个循环引用,导致这些shared_ptr所指向的对象无法被正确删除,因为每个shared_ptr都认为至少还有一个其他shared_ptr指向该对象。而weak_ptr的设计就是为了解决这个问题。
  • weak_ptr不拥有其所指向的对象,它不会增加对象的所有权计数,即shared_ptr中的引用计数。相反,weak_ptr提供了一种方式来观察shared_ptr所管理的对象,但不阻止该对象被销毁。
  • weak_ptr可以从shared_ptr或另一个weak_ptr中创建,但不能直接用来访问对象;它必须首先被锁定为一个shared_ptr,这通过调用weak_ptr的lock()方法实现。如果原始shared_ptr已经被销毁,lock()将返回一个空的shared_ptr。

2、weak_ptr类

在这里插入图片描述
在这里插入图片描述

3、shared_ptr的循环引用问题

  • 在C++中,当使用shared_ptr时,一个常见的问题是循环引用(circular reference),这发生在两个或多个shared_ptr实例相互引用对方。由于每个shared_ptr都认为至少有一个其他shared_ptr正在引用其管理的对象,因此它们的引用计数永远不会降为零,这导致内存无法被正确释放,从而产生内存泄漏。

4、循环引用示例代码

class B;

class A {
public:
	//std::shared_ptr<B> bPtr;
	std::weak_ptr<B> bPtr;
	~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
	std::shared_ptr<A> aPtr;
	~B() { std::cout << "B destroyed\n"; }
};

void Test_shared_ptr5()
{
	std::shared_ptr<A> a = std::make_shared<A>();
	std::shared_ptr<B> b = std::make_shared<B>();

	// 创建循环引用  
	a->bPtr = b;
	b->aPtr = a;
}
  • 上方代码为循环引用问题解决后的代码,循环引用问题的代码为将A类中的bPtr改为shared_ptr类型。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2093620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习(11)---Swin Transformer详解

文章目录 一、引言二、结构三、Patch Merging操作四、W-MSA详解五、SW-MSA详解 一、引言 1. 在原论文中&#xff0c;首先在开头作者就分析&#xff0c;当前的Transformer从NLP迁移到CV上没有大放异彩主要原因集中在&#xff1a;  (1) 两个领域涉及的规模不同&#xff0c;NLP的…

YOLOv9改进策略【模型轻量化】| ShufflenetV2,通过通道划分构建高效网络

一、本文介绍 本文记录的是基于ShufflenetV2的YOLOv9目标检测轻量化改进方法研究。FLOPs是评价模型复杂独的重要指标&#xff0c;但其无法考虑到模型的内存访问成本和并行度&#xff0c;因此本文在YOLOv9的基础上引入ShufflenetV2&#xff0c;使其在在保持准确性的同时提高模型…

十一. 常用类

文章目录 一、包装类1.1 包装类的继承关系1.2 包装类和基本数据类型的转换1.3 包装类与String之间的转换1.4 包装类的常用方法 二、String类2.1 String类的理解和创建对象2.2 String的创建方式2.3 字符串的特性2.4 String的常用方法 三、StringBuffer和StringBuilder类3.1 Stri…

重塑PDF编辑体验:最新在线工具深度评测

现在用PDF的场景多了&#xff0c;随之而来的加速了PDF编辑、转换工具的飞速发展&#xff0c;很多时候因为便捷大家更喜欢使用在线的工具。今天我就分享几款pdf在线编辑工具提高你文档处理效率。 1.福昕PDF编辑器 链接一下>>https://editor.foxitsoftware.cn 在沉浸阅…

Datawhale X 李宏毅苹果书 AI夏令营|机器学习基础之线性模型

1. 线性模型 线性模型是机器学习中最基础和常见的模型之一。在线性模型中&#xff0c;预测变量&#xff08;输入特征&#xff09;和目标变量&#xff08;输出&#xff09;之间的关系被建模为一个线性组合。数学形式可以表示为&#xff1a; 其中&#xff1a;x 是输入特征向量&a…

加速 PyTorch 模型:使用 ROCm 在 AMD GPU 上应用 torch.compile

Accelerate PyTorch Models using torch.compile on AMD GPUs with ROCm — ROCm Blogs 介绍 PyTorch 2.0 引入了一个名为*torch.compile()*的工具&#xff0c;可以极大地加速 PyTorch 代码和模型。通过将 PyTorch 代码转换为高度优化的内核&#xff0c;torch.compile 在现有代…

【深入理解SpringCloud微服务】深入理解微服务配置中心原理,并手写一个微服务配置中心

【深入理解SpringCloud微服务】深入理解微服务配置中心原理&#xff0c;并手写一个微服务配置中心 为什么要使用配置中心配置中心原理如何手写一个配置中心使用PropertySourceLocator监听配置变更&#xff0c;刷新配置 实现一个微服务配置中心服务端库表ConfigCenterController…

全国中学基础信息 API 数据接口

全国中学基础信息 API 数据接口 基础数据&#xff0c;高校高考&#xff0c;提供全国初级高级中学基础数据&#xff0c;定时更新&#xff0c;多维度筛选。 1. 产品功能 2024 年数据已更新&#xff1b;提供最新全国中学学校基本信息&#xff1b;包含全国初级中学与高等中学&#…

JavaWeb:实验一JSP运行环境安装及配置

一、实验目的 1&#xff0e;掌握JSP程序运行环境配置的基本要求。 2&#xff0e;熟悉HTML的常用标签使用以及静态网页的制作。 二、实验性质 验证性实验 三、实验内容 制作一个静态网站的基本页面index.html&#xff0c;要求如下&#xff1a; 1&#xff…

SQL 语言简明入门:从历史到实践

SQL&#xff08;Structured Query Language&#xff09;是数据库领域的核心语言。自20世纪70年代中期由IBM公司开发以来&#xff0c;SQL已经成为全球最广泛使用的数据库管理语言。 本文将以简洁明了的方式为您介绍SQL的历史、基本结构、核心语言组成以及其独特的特点和书写规则…

【软件部署】JumpServer堡垒机搭建及使用

目录 一、linux服务器docker搭建 二、linux服务器单机部署 1.环境安装 2.安装数据库和Redis 3.下载linux安装包并部署 4.安装启动后命令 5.通过浏览器访问部署服务器的地址 三、JumpServer产品使用 1、添加系统用户 2、创建资产 3、将主机/资源进行授权给用户 4、登录…

Day 7:条件编译

GCC编译器 预处理阶段&#xff1a; 1.gcc - E 文件名 &#xff1a;预处理文件 2.gcc - o 文件名 &#xff1a;重命名 gcc -E gcc.c gcc-o gcc.i&#xff1a;生成预处理文件。 vi gcc.i&#xff1a; 作用&#xff1a;展开头文件&#xff0c;宏进行替换。 编译阶段: gcc -…

希尔排序的图解展示与实现

什么是希尔排序 对整个数组进行预排序&#xff0c;即分组排序&#xff1a;按间距为gap分为一组&#xff0c;分组进行插入排序。 预排序的作用与特点 大的数更快地到后面&#xff0c;小的数更快地到前面&#xff1b; gap越大&#xff0c;跳得越快&#xff0c;排完接近有序慢&…

数据结构与算法---排序算法

文章目录 排序选择排序冒泡排序插入排序 希尔排序归并排序快速排序桶排序计数排序基数排序堆排序 排序 排序是指将一组数据按照特定的规则或顺序进行排列&#xff0c;比如一个数组[1, 5, 2, 4, 3]按照从小到大的顺序排列后就是[1,2,3,4,5]。 排序算法&#xff08;Sorting alg…

lay数据表格(table)的多选框限制单选

TOC lay数据表格(table)的多选框限制单选 使用layui弹窗显示表格数据提供选择&#xff0c;最初使用单选框&#xff0c;选中后无法取消勾选&#xff0c;后该成多选框限制成单选&#xff0c;可点击已勾选复选框实现取消功能。 PS&#xff1a;easyui数据表格提供简单实现 多选框…

有哪些数据分析类的软件可供参考?

对于数据分析师来说&#xff0c;掌握以下数据分析工具很有必要&#xff0c;一个好的数据分析工具&#xff0c;可以使得数据分析工作事半功倍&#xff0c;相对于整个数据分析学习流程来说&#xff0c;掌握数据分析工具是学习数据分析的关键。 日常数据分析中&#xff0c;有80%的…

【电力系统】使用电力系统稳定器 (PSS) 和静态 VAR 补偿器 (SVC) 提高瞬态稳定性

摘要 电力系统在面对故障和扰动时&#xff0c;其瞬态稳定性是确保系统安全运行的关键因素。本文探讨了通过使用电力系统稳定器&#xff08;PSS&#xff09;和静态VAR补偿器&#xff08;SVC&#xff09;来提高电力系统瞬态稳定性的策略。通过仿真分析&#xff0c;证明了PSS和SV…

折腾 Quickwit,Rust 编写的分布式搜索引擎 - 可观测性之分布式追踪

概述 分布式追踪是一种跟踪应用程序请求流经不同服务(如前端、后端、数据库等)的过程。它是一个强大的工具&#xff0c;可以帮助您了解应用程序的工作原理并调试性能问题。 Quickwit 是一个用于索引和搜索非结构化数据的云原生引擎&#xff0c;这使其非常适合用作追踪数据的后端…

提升农业信息化水平,C# ASP.NET Vue果树生长信息管理系统带来全新管理体验

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

【算法每日一练及解题思路】找出模式匹配字符串的异位词在原始字符串中出现的索引下标

【算法每日一练及解题思路】找出模式匹配字符串的异位词在原始字符串中出现的索引下标 一、题目&#xff1a;找出模式匹配字符串的异位词在原始字符串中出现的索引下标 二、举例&#xff1a; 两个字符串原始字符串initStr123sf3rtfb,模式匹配字符串regxf3s&#xff0c;找到模…