智能指针(RAII)

news/2024/9/18 21:12:08/ 标签: 后端, c++, visual studio, windows, 开发语言, 智能指针

智能指针(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.ppmy.cn/news/1518617.html

相关文章

如何处理时间序列异常值?理解、检测和替换时间序列中的异常值

异常值的类型 (欢迎来到雲闪世界) 异常值是与正常行为有显著偏差的观察结果。 时间序列可能会因某些异常和非重复事件而出现异常值。这些异常值会影响时间序列分析&#xff0c;并误导从业者得出错误的结论或有缺陷的预测。因此&#xff0c;识别和处理异常值是确保时间序列建模…

【TDesign】如何修改CSS变量

Tdesign的组件想通过style定义样式没效果, 可以通过组件api文档修改, 组件提供了下列 CSS 变量&#xff0c;可用于自定义样式。 比如Cell, https://tdesign.tencent.com/miniprogram/components/cell?tabapi 提供了&#xff1a; –td-cell-left-icon-color 图标颜色 –td-cell…

【Leetcode 2341 】 数组能形成多少数对 —— 去重

给你一个下标从 0 开始的整数数组 nums 。在一步操作中&#xff0c;你可以执行以下步骤&#xff1a; 从 nums 选出 两个 相等的 整数从 nums 中移除这两个整数&#xff0c;形成一个 数对 请你在 nums 上多次执行此操作直到无法继续执行。 返回一个下标从 0 开始、长度为 2 的…

电脑变声器软件哪个好用?最新款实时变声器数据公开!

电脑变声器软件哪个好用&#xff1f;什么场合下需要用到变声器&#xff1f;在派对或朋友聚会中&#xff0c;使用变声器可以模仿各种动物、名人或虚构角色的声音&#xff1b;直播变声搞怪&#xff1b;匿名游戏聊天&#xff1b;电影、动画、电视音效、旁白制作等等&#xff0c;都…

高职院校大数据分析与可视化微服务架构实训室解决方案

一、前言 随着信息技术的飞速发展&#xff0c;大数据已成为推动社会进步与产业升级的关键力量。为了培养适应未来市场需求的高素质技术技能型人才&#xff0c;高职院校纷纷加大对大数据分析与可视化技术的教学投入。唯众&#xff0c;作为国内领先的职业教育解决方案提供商&…

2 Python开发工具:PyCharm的安装和使用

本文是 Python 系列教程第 2 篇&#xff0c;完整系列请查看 Python 专栏。 1 安装 官网下载地址https://www.jetbrains.com.cn/pycharm/&#xff0c;文件比较大&#xff08;约861MB&#xff09;请耐心等待 双击exe安装 安装成功后会有一个30天的试用期。。。本来想放鸡火教程&…

Nginx负载均衡请求队列配置:优化流量管理

在高流量的Web应用场景中&#xff0c;合理地管理进入的请求流量对于保持服务的稳定性和响应性至关重要。Nginx提供了请求队列的配置选项&#xff0c;允许开发者控制进入后端服务器的请求数量。通过配置请求队列&#xff0c;可以在后端服务器达到最大处理能力时&#xff0c;优雅…

005、架构_数据节点

​DN组件总览 ​ DN节点包含进程 dbagent进程:主要提供数据节点高可用、数据导入导出、数据备份恢复、事务一致性、运维类功能、集群的扩缩容、卸数等功能;MySQL进程:主要提供数据一致性、分组管理、快同步复制、高低水位等;

测试岗位应该学什么

以下是测试岗位需要学习的一些关键内容&#xff1a; 1. 测试理论和方法 - 了解不同类型的测试&#xff0c;如功能测试、性能测试、压力测试、安全测试、兼容性测试等。 - 掌握测试策略和测试计划的制定。 2. 编程语言 - 至少熟悉一种编程语言&#xff0c;如 Python、Java…

网络路由介绍,route指令,查询路由表的过程,默认路由

目录 路由 本地主机的路由功能 引入 route指令 查询路由表的过程 介绍 示例 默认路由 注意 路由 本地主机的路由功能 引入 报文经过多个路由器转发至公网,再从公网定位后转发至私网,最终到达目标主机 而报文肯定是要先经过本地主机的 所以本地主机也具有路由功能,也…

django网吧收费管理系统 项目源码26819

摘 要 随着互联网的普及&#xff0c;网吧作为公共互联网接入场所&#xff0c;依旧在许多地区发挥着重要作用。现代网吧不仅仅是提供上网服务的场所&#xff0c;还包括了游戏、社交、休闲等多功能体验。为了提高网吧的服务质量和运营效率&#xff0c;迫切需要一个高效的管理系统…

mysql基础语法——个人笔记

0 前言 以前学习且实践过mysql&#xff0c;但后来用得少&#xff0c;随着岁月更替&#xff0c;对其印象渐浅&#xff0c;所以每次需要用时&#xff0c;都会去再看一眼语法规范&#xff0c;然后才能放心动手操作 然而&#xff0c;在信息爆炸的时代&#xff0c;查语法规范时&am…

ubuntu录屏解决ubuntu下无法播放MP4格式文件的方法

参考 gnome gnome是系统自带的录屏&#xff0c;通过ctrlshiftaltr触发 保存到了视频目录下&#xff0c;webm格式文件。 screencastify 这是一个chrome扩展&#xff0c;&#xff0c;一般不推荐使用 recapp 比gnome自由一些&#xff0c;可以自由屏幕录制。但是无法修改录制…

如何将Dxf文件中的Vertex与相应的polyline关联起来

在处理DXF&#xff08;Drawing Exchange Format&#xff09;文件时&#xff0c;将VERTEX和相应的POLYLINE关联起来是一个常见的需求。这通常涉及解析DXF文件中的几何实体&#xff0c;并确保它们之间的关系正确。以下是一些步骤和示例代码&#xff0c;帮助你实现这种关联&#x…

如果学流式系统你想选一本书,那必须是这本

“如果你关心流式处理和批处理工作的正确性&#xff0c;那么这本书是必读的。它对该主题的讨论是我看到的思考最清晰、最合逻辑的&#xff0c;其思想也被精彩诠释。” ——马丁克莱普曼&#xff08;Martin Kleppmann&#xff09;&#xff0c;剑桥大学 流式系统 如今&#xff0c…

关于mysql的information_schema库表对象

MySQL的information_schema库是一个非常重要的系统数据库&#xff0c;它存储着关于MySQL服务器中所有其他数据库的元数据&#xff08;meta-data&#xff09;。元数据是指关于数据的数据&#xff0c;比如数据库名、表名、列名、数据类型、权限信息等&#xff0c;并不包含实际业务…

Django 后端架构开发:高效测试自动化工具

Django 后端架构开发&#xff1a;高效测试自动化工具 目录 &#x1f6e0; nose&#xff1a;强大的测试框架 &#x1f3ad; faker&#xff1a;模拟数据生成器 &#x1f5a5; PyAutoGUI&#xff1a;跨平台 GUI 自动化测试 &#x1f9ea; coverage&#xff1a;代码覆盖率测量 …

【C++】容器vector常用接口详解

目录 一.vector基本介绍 二.vector的构造&#xff08;constructor&#xff09; 三.vector迭代器&#xff08;iterator&#xff09; 四.vector的三种遍历 1.for循环 2.范围for 3.迭代器&#xff08;正向、反向&#xff09; 五. vector扩容操作 1.reserve 2.resize 六.…

若依微服务集成knife4j实现swagger增强

Knife4j 是一个基于 Swagger 的API文档生成工具,专注于为 Java 开发者提供更好的接口文档管理和展示功能。它提供了一种简单、直观的方式来展示和管理 API 文档,使开发者能够更方便地查看和理解接口的定义、参数、返回结果等信息。 1、增加knife4j全局版本号和依赖包 在根目录…

Kafka入门:从零开始了解分布式流处理平台

什么是Kafka Apache Kafka是由LinkedIn公司开发&#xff0c;后来由Apache软件基金会维护的一个分布式、分区、多副本的基于ZooKeeper协调的分布式消息系统。Kafka不仅是一个消息队列&#xff0c;还是一个强大的流处理平台&#xff0c;它能够实时地处理大量数据&#xff0c;满足…