【C++入门到精通】智能指针 [ C++入门 ]

在这里插入图片描述

阅读导航

  • 引言
  • 一、什么是智能指针
  • 二、为什么需要智能指针
  • 三、内存泄漏
    • 1. 什么是内存泄漏,内存泄漏的危害
    • 2. 内存泄漏的示例,以及解决方法
    • 3. 内存泄漏分类
      • (1)堆内存泄漏(Heap leak)
      • (2)系统资源泄漏
    • 4. 如何检测内存泄漏
  • 四、智能指针的使用及原理
    • 1. RAII机制
      • (1)概念
      • (2)原理
      • (3)优点
    • 2. RAII机制下最基本的智能指针框架
  • 温馨提示

引言

在C++编程中,内存管理一直是一个重要的话题。手动分配和释放内存可能会导致各种问题,例如内存泄漏和悬挂指针,这些问题往往会导致程序崩溃或产生不可预测的结果。为了解决这些问题,C++提供了一种称为智能指针的机制,它可以自动管理内存分配和释放,从而避免了手动管理内存所带来的许多问题

本文将深入探讨C++中的智能指针,介绍智能指针的基本概念、类型和用法,通过深入研究C++智能指针的相关知识,我们将能够更好地理解如何编写安全、可靠的C++代码,并避免许多与手动内存管理相关的问题。无论您是初学者还是有经验的开发人员,本文都将为您提供宝贵的信息和指导,帮助您在C++编程的旅程中更上一层楼。让我们一起探索C++智能指针的精彩世界!

一、什么是智能指针

智能指针是一种可以管理动态分配的内存的 C++ 类,它能够自动释放指向的对象所占用的内存,在 C++11 中引入。这种指针可以避免程序员手动释放内存的错误和内存泄露的问题

智能指针通过封装指针,提供了一组安全而方便的操作接口来管理指针所引用的资源,例如内存。它们往往通过引用计数的方式记录对象的引用次数,并在引用次数为 0 时自动释放内存。常见的智能指针有 std::unique_ptrstd::shared_ptr,后面我们会一个一个的介绍。

使用智能指针的好处包括:动态内存的分配和回收更加安全、高效;不再需要手动进行内存管理,提高代码的可读性、可维护性;减少了出现内存泄露和野指针的概率等

二、为什么需要智能指针

下面我们先分析一下下面这段程序有没有什么内存方面的问题?代码中提出来了三个问题,进一步解释了智能指针存在的意义

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{// 1、如果p1这里new 抛异常会如何?int* p1 = new int;/*如果在p1 = new int;这里使用new分配内存时抛出异常,由于该语句之前没有针对异常的处理机制,异常将传播到调用函数的地方(即Func()函数)。此时,p1指针将保持为空指针,并且由于没有正确释放内存的机会,将会导致内存泄漏。*/// 2、如果p2这里new 抛异常会如何?int* p2 = new int;/*如果在p2 = new int;这里使用new分配内存时抛出异常,与第一个问题类似,异常将传播到调用函数的地方(即Func()函数)。此时,p1指针将指向已分配的内存,而p2指针将保持为空指针。同样,由于没有正确释放p1指针指向的内存,将会导致内存泄漏。*/// 3、如果div调用这里又会抛异常会如何?cout << div() << endl;/*如果在div()函数的执行过程中抛出异常,该异常会立即终止当前函数的执行,并被传播到调用函数的地方(即Func()函数)。由于Func()函数没有处理这个异常的机制,它将被传播到main()函数中的异常处理部分。在main()函数的异常处理部分,异常对象的what()方法会被调用,将异常的信息打印到控制台。*/delete p1;delete p2;
}
int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

总的来说,这段代码没有充分处理可能抛出的异常情况。智能指针就可以很好的解决上面的问题,使用智能指针来管理动态内存,以避免忘记释放内存或者因为异常而导致内存泄漏的问题。

三、内存泄漏

1. 什么是内存泄漏,内存泄漏的危害

内存泄漏是指在程序运行中,由于某些原因导致已经动态分配的内存空间没有被正确释放或回收,从而造成系统内存的浪费和不足。当内存泄漏严重时,会导致系统崩溃或者变得非常缓慢

内存泄漏通常是由于程序员在使用动态内存分配函数(如 newmalloc等)时出现错误所引起的。当使用动态内存分配函数获得内存空间后,如果在使用完毕后没有及时释放,这部分内存就会成为无用内存,或者说是“死内存”,这样就会导致内存的浪费,最终导致内存耗尽。内存泄漏还可能会导致程序运行时的性能下降,甚至影响到程序的稳定性。

内存泄漏的解决方法通常是手动释放内存,或者使用智能指针等自动化工具来管理内存。此外,在编写代码时,应该避免出现内存泄漏的情况,例如尽量避免使用裸指针(raw pointer)、避免循环引用等。

2. 内存泄漏的示例,以及解决方法

void MemoryLeaks()
{// 1.内存申请了忘记释放int* p1 = (int*)malloc(sizeof(int));int* p2 = new int;// 2.异常安全问题int* p3 = new int[10];Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.delete[] p3;
}
  1. 在第一部分,我们使用了 mallocnew 分别分配了两个整型指针 p1p2 的内存空间,但是在后续代码中没有使用 freedelete 来释放这些内存。这意味着这两个内存块将一直保留在堆上,造成内存泄漏。为了避免内存泄漏,应该在不再需要使用这些指针时,使用 freedelete 来显式释放内存。

  2. 在第二部分,我们使用了 new 来分配了一个整型数组 p3 的内存空间,但是在调用 Func() 函数之后,如果该函数抛出异常,那么 delete[] p3 将无法执行,从而导致 p3 指向的内存没有被释放。这也是一种内存泄漏情况。为了解决这个问题,可以使用异常处理机制(例如 try-catch 块),确保在发生异常时也能够正确释放内存。

修复这些内存泄漏问题的方法如下

void MemoryLeaks()
{// 1.内存申请了忘记释放int* p1 = (int*)malloc(sizeof(int));int* p2 = new int;// 在不再需要使用 p1 和 p2 时,释放内存free(p1);delete p2;// 2.异常安全问题int* p3 = new int[10];try {Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.} catch (...) {// 在发生异常时,确保能够释放内存delete[] p3;throw; // 继续抛出异常}delete[] p3;
}

这样,通过显式地释放内存,并在发生异常时进行异常处理,可以避免上述代码中的内存泄漏问题。

3. 内存泄漏分类

🚩C/C++程序中一般我们关心两种方面的内存泄漏:堆内存泄漏(Heap leak)和系统资源泄漏

(1)堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生堆内存泄漏

(2)系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

4. 如何检测内存泄漏

✅检测内存泄漏可以使用一些工具和技术。下面是几种常用的方法:

  1. 静态代码分析:使用静态分析工具(例如Clang Static Analyzer,Cppcheck等)对代码进行扫描,以检测潜在的内存泄漏问题。这些工具可以在编译期间检查代码,并提供警告或错误提示。

  2. 重载内存分配函数:通过重载 newdelete 运算符,可以跟踪内存的分配和释放情况。你可以重载全局版本的 newdelete,或者在需要跟踪的类中重载它们。通过在这些重载函数中添加自定义的日志记录或计数机制,可以检测内存泄漏。

  3. 自定义内存管理器:实现自己的内存管理器可以更好地控制内存的分配和释放过程,并能够记录和追踪分配的内存。通过在分配和释放内存时维护一个内存块的列表,可以检测内存泄漏情况。

  4. 内存泄漏检测工具:某些集成开发环境(IDE)和调试器提供内置的内存泄漏检测功能。—>>🔴内存泄露检测工具

四、智能指针的使用及原理

1. RAII机制

(1)概念

RAII(Resource Acquisition Is Initialization)是一种C++编程技术,它通过将资源的获取和释放与对象的生命周期绑定在一起,以确保资源在对象创建时获取,在对象销毁时释放。这种技术利用了C++对象的构造函数和析构函数的调用机制,使得资源的管理变得更加简洁、安全和可靠。

(2)原理

使用RAII的关键在于将资源的获取和释放操作分别放置在对象的构造函数和析构函数中。当对象被创建时,构造函数负责获取资源并进行必要的初始化工作;当对象被销毁时,析构函数负责释放资源并进行清理工作。由于C++保证在对象销毁时析构函数会被自动调用,所以资源的释放也就得到了保证。

(3)优点

RAII技术的优点如下:

  1. 简洁性:通过对象的自动构造和析构,减少了手动管理资源的代码量,使得程序更加简洁易读。
  2. 安全性:确保资源在适当的时候被释放,避免了常见的资源泄漏和错误状态的产生。
  3. 可靠性:无论在何时何地发生异常或提前退出,都能够保证资源的正确释放,提高程序的可靠性。
  4. 可扩展性:通过继承和组合等方式,可以方便地扩展和管理更复杂的资源。

常见的使用RAII技术的例子包括使用智能指针管理动态内存、使用文件对象进行文件操作、使用互斥锁类进行线程同步等。通过合理运用RAII技术,可以有效地提高代码的可维护性、可读性和可靠性,是现代C++编程中的重要技术之一。

2. RAII机制下最基本的智能指针框架

#include <iostream>
#include <memory>
using namespace std;// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};// 除法函数,可能抛出异常
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{SmartPtr<int> sp1(new int); // 使用SmartPtr管理动态分配的int对象SmartPtr<int> sp2(new int); // 使用SmartPtr管理动态分配的int对象cout << div() << endl; // 调用div函数进行除法运算// 在Func函数结束时,SmartPtr析构函数会自动释放资源
}int main()
{try {Func(); // 调用Func函数}catch(const exception& e){cout << e.what() << endl; // 捕获并输出异常信息}return 0;
}

🚨🚨注意:上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为指针可以解引用,也可以通过->去访问所指空间中的内容,因此: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;}//重载`->`
private:T* _ptr;
};

重载operator*opertaor->,具有像指针一样的行为,这样才能像指针一样使用。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sqoop的增量数据加载策略与示例

当使用Apache Sqoop进行数据加载时&#xff0c;增量数据加载策略是一个关键的话题。增量加载可以仅导入发生变化的数据&#xff0c;而不必每次都导入整个数据集&#xff0c;这可以显著提高任务的效率。本文将深入探讨Sqoop的增量数据加载策略&#xff0c;提供详细的示例代码&am…

大语言模型面试问题

自己在看面经中遇到的一些面试题&#xff0c;结合自己和理解进行了一下整理。 transformer中求和与归一化中“求和”是什么意思&#xff1f; 求和的意思就是残差层求和&#xff0c;原本的等式为y H(x)转化为y x H(x)&#xff0c;这样做的目的是防止网络层数的加深而造成的梯…

管理软件供应链中网络安全工具蔓延的三种方法

软件开发组织不断发展&#xff0c;团队成长&#xff0c;项目数量增加。技术堆栈发生变化&#xff0c;技术和管理决策变得更加分散。 在这一演变过程中&#xff0c;该组织的 AppSec 工具组合也在不断增长。在动态组织中&#xff0c;这可能会导致“工具蔓延”。庞大的 AppSec 工…

Java--RSA非对称加密的实现(使用java.security.KeyPair)

文章目录 前言实现步骤测试结果 前言 非对称加密是指使用不同的两个密钥进行加密和解密的一种加密算法&#xff0c;调用方用使用服务方提供的公钥进行加密&#xff0c;服务方使用自己的私钥进行解密。RSA算法是目前使用最广泛的公钥密码算法。Java提供了KeyPairGenerator类要生…

2024年AMC8模拟考试实测流程、注意事项和常见问题

和往年的AMC8比赛一样&#xff0c;在正式比赛的前一周左右会开放两天的模拟考试时间&#xff0c;AMC8的主办方建议所有的参赛选手重视且参加模拟考试&#xff0c;以测试设备、熟悉流程&#xff0c;避免将来正式考试不小心违规&#xff0c;或者设备不给力。 2024年的AMC8模拟考…

OFBiz RCE漏洞复现(CVE-2023-51467)

漏洞名称 Apache OFBiz 鉴权绕过导致命令执行 漏洞描述 Apache OFBiz是一个非常著名的电子商务平台&#xff0c;是一个非常著名的开源项目&#xff0c;提供了创建基于最新J2EE/XML规范和技术标准&#xff0c;构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式…

五、带登录窗体的demo

做了一个简单的带登录窗体的demo&#xff0c;有用户名和密码不能为空的验证&#xff0c;原理是在main.cpp的主函数入口处&#xff1a; 1、将默认的MainWindow主窗体注释。 2、新建一个formlogin登录窗体&#xff0c;在主函数中先运行登录窗体。 3、在登录窗体中引用MainWind…

Android json功能解析

1. 简介 JAVAScript Object Notation是一种轻量级的数据交换格式具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案&#xff08;有点类似于正则表达式 &#xff0c;获得了当今大部分语言的支持&#xff09;。  JSON采用兼容性很高的文本格式&#xf…

路由器02_静态路由DHCP

一、静态路由 &#xff11;、静态路由特点 由管理员手工配置&#xff0c;是单向的&#xff0c;缺乏灵活性 &#xff12;、默认路由 默认路由是一种比较特殊静态路由&#xff0c;一般用于末节&#xff08;末梢&#xff09;网络&#xff0c;直接指定目标为任何地方 二、静态…

nodejs+vue+ElementUi音乐分享社交网站77l8j

本文介绍的系统主要分为两个部分&#xff1a;一是前台界面&#xff1a;用户通过注册登录可以实现音乐播放、新闻浏览、留言评论等功能&#xff1b;另一个是后台界面&#xff1a;音乐网站管理员对用户信息进行管理&#xff0c;上传更新音乐资源&#xff0c;发布最新音乐资讯等功…

Casper Network (CSPR)2024 年愿景:通过投资促进增长

Casper Network (CSPR&#xff09;是行业领先的 Layer-1 区块链网络之一&#xff0c;通过推出了一系列值得关注的技术改进和倡议&#xff0c;已经为 2024 年做好了准备。 在过去的一年里&#xff0c;Casper Network (CSPR&#xff09;不断取得里程碑式的进展&#xff0c;例如推…

高性能RPC框架解密

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

linux安装系统遇到的问题

这两天打算攻克下来网络编程&#xff0c;发现这也确实是很重要的一个东西&#xff0c;但我就奇了怪了&#xff0c;老师就压根没提&#xff0c;反正留在我印象的就一个tcp/ip七层网络。也说正好&#xff0c;把linux命令也熟悉熟悉&#xff0c;拿着我大一课本快速过过 连接cento…

一个简易的PHP论坛系统

一个简易的PHP论坛系统 php课程设计&#xff0c;毕业设计 预览 技术 bootstrap 4.x jquery css php mysql 5.7 目录结构 登录 管理员 admin/123456 测试用户 user1/123456 更多文章和源码获取查看

【Java SE语法篇】8.面向对象三大特征——封装、继承和多态

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 1. 封装1.1 封装的概念1.2 为什么封装1.3 封装的实现…

PingCAP 受邀参加 FICC 2023,获 Open100 世纪全球开源贡献奖

2023 年 12 月&#xff0c;2023 国际测试委员会智能计算与芯片联邦大会&#xff08;FICC 2023&#xff09;在海南三亚举办&#xff0c;中外院士和数十位领域专家莅临出席。 大会现场 &#xff0c;开放源代码促进会创始人 Bruce Perens 颁发了 Open100 世纪全球开源贡献奖&…

C#,入门教程(17)——条件语句(if-else)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(16)——可变数据类型&#xff08;var&#xff09;的基础知识与使用禁忌https://blog.csdn.net/beijinghorn/article/details/124032216 程序的核心是逻辑。 逻辑的核心是布尔条件表达式。 逻辑的主要体现形式之一是 if-else 语句…

鸿蒙开发已解决-arkts编译报错-arkts-limited-stdlib错误

文章目录 项目场景:问题描述原因分析:解决方案:适配指导案例此Bug解决方案总结项目场景: arkts编译报错-arkts-limited-stdlib错误。 我用Deveco studio4.0 beta2开发应用,报arkts-limited-stdlib错误 报错内容为: ERROR: ArKTS:ERROR File: D:/prRevivw/3792lapplica…

在线的货币兑换平台源码下载

在线的货币兑换平台&#xff0c;可帮助全球各地的个人和企业将货币从一种货币兑换为另一种货币。该货币兑换平台是 Codecanyon 中最先进的脚本。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/88728084