都说 C++ 没有 GC,RAII: 那么我算个啥?(赠书福利)

这篇具有很好参考价值的文章主要介绍了都说 C++ 没有 GC,RAII: 那么我算个啥?(赠书福利)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

*以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/7A9-tGZxf4w_7eZl3OUQ4A

都说 C++ 没有 GC,RAII: 那么我算个啥?(赠书福利)

学过 Java、C# 或者其他托管语言(managed languages)的同学,回过头来看 C++ 的时候,第一反应就是 C++ 没有自动垃圾回收器(GC),而不能充分利用的资源被称为垃圾。

那么 C++ 真的不能自动回收垃圾吗?带着这个疑问我们来看看一般 C++ 程序都是怎样回收资源的。

内存在计算机系统中是有限的资源,通常申请内存和释放内存是这样子的,假设有个被调用的函数 function():

void function()
{
    int *p = new int; // 申请内存

    // 资源申请下来了,不玩有个 p 用?
    // do something

    delete p; // 释放内存
}

这段示例代码在 function() 函数开始的时候申请了一块内存,大小对应于 int 类型,然后在函数结束的时候释放它。通常来说,这看起来很OK,没毛病,但是,如果遇到了下面几种情况呢?

  • 程序如果中途有逻辑让它提前退出 function() 函数
  • 发生了异常而没有被捕获到

那么在函数尾部执行释放内存的动作有几率不会被执行,意味着发生也会内存泄漏。像上面这段代码,如果调用的次数不多也不碍事,不过,如果循环调用 function(),这时泄露的内存资源会不断累积,而且一直被浪费掉,期间系统无法再次使用这些被浪费的内存,直到进程被终止,严重的话,会导致系统资源被耗尽,跑着跑着系统都崩溃了。这种 bug 在 C 范式的编程语言中真的很常见。

RAII 是什么

众所周知 C++ 具有面向对象的特性,在初始化类对象的时候,系统会调用类构造函数。如果类对象是存放在栈空间的话,比如声明为局部变量,那么当类对象超出生命周期时,比如退出局部变量的作用域,系统会调用这个对象的类析构函数;如果类对象是存放在堆空间的话,比如通过 new 操作符创建的类对象,那么当类对象被销毁时,比如对对象执行 delete 操作,系统同样会调用类析构函数。

C++ 的这个特性可以用来解决上面提到的资源泄露问题,怎么利用呢?

modern C++ 实践建议优先把资源存放在栈上。如果只是个变量类型,完全可以用局部变量的形式定义声明,这样代码块在退出后系统自动回收栈上的资源。

对上面的函数 function() 修改

void function()
{
    // 声明定义为局部变量,资源存储在栈区
    int data = 0;

    // do something with data

    // 函数退出时,自动释放 data 占用的空间
}

当资源比较占空间时,需要在堆上分配资源,可以通过指针引用它,资源的申请放在类的构造函数里,然后在析构函数里释放。下面举个例子

class Helper
{
private:
    int* data;
public:
    Helper() {
        data = new int; // 在堆上申请内存
    }
    ~Helper() {
        delete data; // 释放堆上申请的内存
    }
    void do_something_with_data() {}
};

void function()
{
    // 声明定义为局部变量,对象存储在栈区
    // 调用 Helper 类构造函数在堆上申请资源
    Helper help;

    // 通过对象 help 调用成员 data
    // 如果 data 是 Helper 私有成员
    // 在类外面必须通过类成员方法调用 data
    help.do_something_with_data();

    // 函数退出时,自动释放 help 对象占用的栈空间
    // 就算发生了异常或者中途退出都会执行这一步
    // help 对象被销毁时,调用 Helper 类析构函数
    // Helper 类析构函数释放已申请的堆上资源
}

利用这种特性的行为被 C++ 发明人称呼为 RAII,英文全称是「resource acquisition is initialization」,中文翻译过来是「资源获取即是初始化」。而我喜欢把它叫做上下文管理,实现资源申请释放的类叫做上下文管理器(context manager)。

经典实践--智能指针

上面的示例代码写起来略显啰嗦,为了推广这种设计核心思路和简化代码编写,在 C++ 11 之后标准库里添加了 unique_ptr。

unique_ptr 属于 Smart Points 中的一种,Smart Points 在国内通常翻译为「智能指针」。智能指针负责管理和释放资源。上面的 function() 函数可以改成这样子

#include <memory>
void function()
{
    // 实例化智能指针对象,输入需要被管理的内存首地址
    // 对象为局部变量,存储在栈区
    std::unique_ptr<int> data(new int);

    // 智能指针对象就像普通指针一样调用
    printf("data=%d\n", *data);

    // 函数退出时,自动释放 data 对象占用的栈空间
    // 就算发生了异常或者中途退出都会执行这一步
    // data 对象被销毁时,同步释放被管理的内存资源
}

可见,用了智能指针后,不需要像之前那样定义类 Helper (上下文管理器)了,代码清爽很多。

不过,上面的示例代码中有个地方需要注意,在实例化智能指针对象时必须传入内存地址,有没有其它更好的方式设置被管理的内存地址?

有的,C++ 14 之后标准库添加了 make_unique,演示一下怎么用文章来源地址https://www.toymoban.com/news/detail-448523.html

std::unique_ptr<int> data = std::make_unique<int>();

到了这里,关于都说 C++ 没有 GC,RAII: 那么我算个啥?(赠书福利)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • C++面试八股文:什么是RAII?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第13面: 面试官:什么是 RAII ? 二师兄: RAII 是 Resource Acquisition Is Initialization 的缩写。翻译成中文是资源获取即初始化。 面试官: RAII 有什么特点和优势? 二师兄:主要的特点是,在对象初始化时获取资源,在对象析构时释放

    2024年02月08日
    浏览(44)
  • 百度的文心一言 ,没有想像中那么差

    我们用 robin 的演示例子来对比一下 文心一言和 ChatGPT 的真实表现(毕竟发布会上是录的)。 注意,我使用的 GPT 版本是 4.0 文学创作 1 三体的作者是哪里人? 文心一言: ChatGPT: 嗯,中文表现上文心一言更好。 2 电视剧三体的演员都有谁? 文心一言: ChatGPT: 关于这个问题

    2024年02月13日
    浏览(25)
  • 【智能合约实战】——入门级DAPP,没有想象中那么难

    最近我司准备引入区块链技术,打算将整车上链,学习新技术的机会千载难逢,于是乎加入智能合约研究小组,以下是我一周了解和学习的入门级小成果 Dapp就是去中心化应用程序的简称 ,是以某种方式连接到区块链的在线应用程序。从理论上讲,dapp是运行在节点服务器上面

    2024年02月05日
    浏览(26)
  • C 语言里面的 extern “C“ ,并没有那么简单!

      本文详细解析 extern \\\"C\\\" 的底层原理与实际应用。 在你工作过的系统里,不知能否看到类似下面的代码。   这好像没有什么问题,你应该还会想: “ 嗯 ⋯ 是啊,我们的代码都是这样写的,从来没有因此碰到过什么麻烦啊~ ” 。 你说的没错,如果你的头文件从来没有被任

    2024年02月07日
    浏览(27)
  • 现在低代码平台推进阻力那么大,有没有最好的解决办法?

    低代码是一种快速设计和开发软件应用程序并且手动编码最少的方法。通过在图形界面中使用可视化建模来组装和配置应用程序,开发人员可以跳过所有基础架构让开发速度提升起来。 1.“在踏出一步之前,首先考虑能否退回去” 现在低代码平台,功能性能这些先不说,能不

    2024年02月11日
    浏览(22)
  • 用ChatGPT帮我进行SQL调优,sql 调优再也没有那么难了

    近期由于订单量激增,我们的 ERP 系统 订单查询效率骤降! 查询半年内的 300万数据就要卡到 50多秒才能出结果(有时要一分多钟)。 而订单查询这块由于系统迭代原因,导致查询条件十分复杂, 索引也已经优化到了极限,不能再通过加索引解决问题。 实际业务中,相信很

    2024年02月06日
    浏览(34)
  • JSONP的安全性较差,那么在跨域情况下,有没有其他更安全的替代方案呢?

    在跨域情况下,为了保证安全性,有几种更安全的替代方案可以考虑使用: 1:CORS(Cross-Origin Resource Sharing): CORS 是一种现代化的跨域解决方案,通过在服务器端设置响应头来控制跨域访问。 服务器可以配置允许跨域请求的来源(域名)、请求方法和头部信息等,以确保仅

    2024年02月03日
    浏览(24)
  • ros中常见问题处理:延迟问题解决方法、订阅的数据感觉比发布的数据要多;如果没有正在接收消息,那么状态如何获取?

    在ROS中,消息的发布和订阅是异步的,也就是说,当你调用pub.publish(output_msg)发布消息时,该函数会立即返回,并不会等待所有订阅者接收消息。因此,如果你的程序出现延迟,可能是由于某些原因导致消息被堵塞或丢失。 以下是几种可能导致延迟的原因和解决方法: 1,消

    2024年02月06日
    浏览(30)
  • 电压和电流反馈判别及例子,绝对让你通透,其实也没有那么难,一次就看懂!从此终于搞懂了电压反馈和电流反馈!

    一个简单粗暴的判断方法: 先看反馈是否直接连到Uo输出端(若不是直接从输出端引出,则为电流反馈) 再假设输出电压Uo为零,或者令负载电阻RL两端电压为0 ,然后看反馈信号是否存在。 若反馈信号不存在了,说明反馈信号与输出电压成比例,是电压反馈。 否则是电流反

    2024年02月09日
    浏览(35)
  • C++RAII内存管理技术

    C++在引入异常机制后, 代码执行流的跳转 变得难以预料,如果使用普通的指针进行内存管理,很难避免 内存泄漏 的问题(执行流跳转导致堆区资源无法被释放) RAII技术指的是 利用对象的生命周期来管理内存资源 ,就堆区内存资源的管理而言,指的就是:将指针封装在类中,在 类对象

    2024年02月12日
    浏览(26)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包