RAII(Resource Acquisition Is Initialization)机制

RAII(Resource Acquisition Is Initialization)机制

1. 什么是 RAII?

🌟 RAII(资源获取即初始化,Resource Acquisition Is Initialization)C++ 语言中的一种管理资源的编程技巧
RAII 使资源(如内存、文件句柄、线程、锁等)与对象的生命周期绑定
这意味着:

  • 对象构造时(constructor)申请资源
  • 对象析构时(destructor)自动释放资源

2. RAII 解决了什么问题?

RAII 解决了 手动管理资源 的各种问题,例如:

  • 忘记释放资源(导致内存泄漏文件句柄泄漏
  • 异常导致资源释放失败(如果函数 throw 异常,资源可能不会被释放)
  • 资源管理逻辑分散,代码难以维护

RAII 确保资源不会泄漏,并且异常不会破坏资源管理,从而 提高代码的安全性和可维护性


3. RAII 代码示例

RAII 典型的做法是:使用类的构造函数获取资源,使用析构函数释放资源

🔥 示例 1:FILE 文件句柄管理(避免 fopen() 忘记 fclose()

#include <iostream>
#include <cstdio>class FileHandler {
public:explicit FileHandler(const char* filename) {file_ = std::fopen(filename, "w");if (!file_) {throw std::runtime_error("无法打开文件!");}}~FileHandler() {  // RAII: 在析构函数中释放资源if (file_) {std::fclose(file_); std::cout << "文件已关闭\n";}}void write(const char* text) {if (file_) {std::fprintf(file_, "%s", text);}}private:FILE* file_; // 资源:文件指针
};int main() {try {FileHandler fh("example.txt");  // 构造时打开文件fh.write("Hello, RAII!\n");} catch (const std::exception& e) {std::cerr << "异常:" << e.what() << std::endl;} // FileHandler 对象 `fh` 作用域结束后,自动释放文件资源
}

📌 这里的 RAII 机制:

  • 构造函数 (FileHandler::FileHandler()) 申请 fopen() 资源
  • 析构函数 (FileHandler::~FileHandler()) 释放 fclose() 资源
  • 即使异常发生,析构函数依旧会执行,确保不会忘记释放资源

🔥 示例 2:智能指针(避免 new 后忘记 delete
在 C++98 及之前,动态分配对象需要手动使用 new/delete

void bad_example() {int* p = new int(10); // 申请内存throw std::runtime_error("发生异常!"); // Oops! `delete p;` 没有执行,内存泄漏!delete p; // 永远不会执行
}

RAII 解决这个问题,C++11 以后我们可以使用 std::unique_ptr

#include <memory>
void good_example() {std::unique_ptr<int> p = std::make_unique<int>(10); // RAII:构造时申请资源throw std::runtime_error("发生异常!"); // 资源仍然会被自动释放,不会泄漏!
} // 作用域结束,自动调用 `unique_ptr` 析构函数,释放 `int*`

📌 这里的 RAII 机制:

  • std::unique_ptr<int> 绑定了 new int(10) 的生命周期
  • 异常发生时,unique_ptr 自动释放 int*,不会导致内存泄漏

🔥 示例 3:C++ std::lock_guard 保护互斥锁
手动加锁和解锁,非常容易遗漏:

std::mutex mtx;
void bad_function() {mtx.lock();  // 手动加锁throw std::runtime_error("发生异常!");  // Oops! 没有 `unlock()`,死锁风险!mtx.unlock();
}

RAII 方式(使用 std::lock_guard):

#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void safe_function() {std::lock_guard<std::mutex> guard(mtx); // RAII:构造时加锁,析构时自动解锁std::cout << "安全访问共享资源\n";// 作用域结束 `lock_guard` 析构时,自动解锁,避免死锁风险
}

4. LoanedCommandInterface 是如何使用 RAII 的?

ROS 2 hardware_interface::LoanedCommandInterface 也是 RAII 设计模式 的一个典型应用:

class LoanedCommandInterface {
public:explicit LoanedCommandInterface(CommandInterface* cmd): command_interface_(cmd) {}~LoanedCommandInterface() { command_interface_ = nullptr; } // RAII 释放资源double get_value() { return command_interface_->get_value(); }void set_value(double value) { command_interface_->set_value(value); }private:CommandInterface* command_interface_;
};

📌 RAII 这里的作用:

  • 构造函数 LoanedCommandInterface() 申请硬件接口
  • 析构函数 ~LoanedCommandInterface() 自动释放硬件接口
  • 即使异常发生,析构仍然会执行,防止资源泄漏

5. 什么时候使用 RAII?

✅ 当你需要手动管理资源(如 内存、文件、锁、网络连接、数据库连接)时,尽量使用 RAII:

  • 替代 new/delete —— 使用 std::unique_ptr
  • 替代 malloc/free —— 使用 std::vector(内部自动管理 new[]/delete[]
  • 处理文件 —— 用 std::fstream,避免 fopen/fclose
  • 管理线程同步 —— 用 std::lock_guard 自动管理 std::mutex

6. RAII 适用于 C++,但在 C 语言中常见吗?

C 语言本身 不支持 RAII,因为 C 没有构造函数/析构函数,必须手动释放资源:

FILE* file = fopen("data.txt", "w");
if (!file) { return -1; }
// 使用文件...
fclose(file);  // 记得手动释放资源

在 C++ 里,我们可以使用 RAII 方式,减少手动释放资源的风险

std::ofstream file("data.txt");

文件会在 std::ofstream 对象销毁时 自动关闭,避免了 C 语言里可能出现的 资源泄漏


7. 总结

🔹 RAII(资源获取即初始化)是 C++ 的重要设计模式
🔹 在构造函数获取资源,在析构函数释放资源,防止泄漏
🔹 RAII 适用于内存(智能指针)、文件、锁、线程、数据库连接等
🔹 std::unique_ptrstd::vectorstd::lock_guard 都是 RAII 典型应用
🔹 hardware_interface::LoanedCommandInterface 也是基于 RAII 的设计

🚀 RAII 增强代码的安全性、可维护性,尽量在 C++ 编程中使用 RAII! 🚀

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

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

相关文章

【kafka系列】日志存储设计 消息写入、读取

目录 日志存储设计 1. 日志存储的目录结构 2. 日志内容格式设计 3. 日志索引设计 4. 设计优势 消息写入流程 示例 流程图 消息读取流程 示例 关键设计细节 流程图 日志存储设计 Kafka的日志存储是其高吞吐、持久化能力的核心设计&#xff0c;其结构包含目录组织、…

vue3.x 自定义hook函数详细解读

1. 什么是自定义 Hook 函数&#xff1f; 自定义 Hook 函数是一个封装了逻辑的 JavaScript 函数&#xff0c;它可以使用 Vue 3 的 Composition API 提供的响应式数据和生命周期钩子。通过自定义 Hook&#xff0c;你可以将组件的逻辑拆分成更小、更可复用的单元。 特点&#xf…

是时候说再见了

说再见 2018 to 2025 2018&#xff1a;学习 2018年开始读研。师兄师姐们说可以写写CSDN博客&#xff0c;对找工作也有帮助。于是在12月4日&#xff0c;发布了自己的第一篇文章[翻译] 神经网络与深度学习 首页 - Index。当时还在学习各种基础知识&#xff0c;看到了这个英文文…

蓝桥杯篇---IAP15F2K61S2定时器

文章目录 前言简介定时器的工作模式1.模式02.模式13.模式24.模式3 定时器的寄存器1.TMOD2.TCON3.THO/TL04.TH1/TL1 定时器的使用步骤1.配置TMOD2.设置初值3.启动定时器4.使能中断5.编写中断服务函数 示例代码&#xff1a;定时器的基本使用代码说明示例代码&#xff1a;定时器1用…

2D 游戏艺术、动画和光照

原文&#xff1a;https://unity.com/resources/2d-game-art-animation-lighting-for-artists-ebook 笔记 用Tilemap瓷砖大小为1单元&#xff0c;人物大小在0.5~2单元 PPU &#xff1a;单位像素 pixels per unit 2160 4K分辨率/ 正交相机size*2 完整屏幕显示像素点 有骨骼动…

HTML的入门

一、HTML HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是一种用来告知浏览器如何组织页面的标记语言。 超文本&#xff1a;就是超越了文本&#xff1b;HTML不仅仅可以用来显示文本(字符串、数字之类)&#xff0c;还可以显示视频、音频等…

C语言:指针详解

C语言&#xff1a;指针详解 1&#xff1a;指针的基本概念1&#xff1a;什么是指针2&#xff1a;为什么要引入指针3&#xff1a;指针的作用4&#xff1a;指针的类型 2&#xff1a;指针的声明与初始化1&#xff1a; 指针的声明2&#xff1a; 指针的初始化 3&#xff1a;指针的操作…

Spring Boot “约定大于配置”

什么是“约定大于配置”&#xff1f; “约定大于配置”是一种简化开发的设计理念。简单来说&#xff0c;就是框架默认提供了常见的配置和行为&#xff0c;开发者只需要按照约定来编写代码&#xff0c;避免了繁琐的配置&#xff0c;只在需要时进行定制和调整。这种理念在Spring…

redis sentinel模式 与 redis 分片集群 配置

Redis 最低为5.0版本&#xff0c;以下为6.2.6版本信息。 模式 高可用性 数据分片 部署复杂度 适用场景 Sentinel 模式 高 无 中等 中小规模&#xff0c;需要高可用性 集群模式 高 支持 复杂 大规模&#xff0c;需要高…

HCIA项目实践---OSPF的基本配置

9.5.12 OSPF的基本配置 &#xff08;所搭环境如上图所示&#xff09; A 先配置IP地址 (先进入路由器R1的0/0/0接口配置IP地址&#xff0c;再进入环回接口配置IP地址) &#xff08;配置R2路由器的0/0/0和0/0/1以及环回接口的IP地址&#xff09; &#xff08;置R3路由器的0/0/0接…

【MyBatis】预编译SQL与即时SQL

目录 1. 以基本类型参数为例测试#{ }与${ }传递参数的区别 1.1 参数为Integer类型 1.2 参数为String类型 2. 使用#{ }传参存在的问题 2.1 参数为排序方式 2.2 模糊查询 3. 使用${ }传参存在的问题 3.1 SQL注入 3.2 对比#{ } 与 ${ }在SQL注入方面存在的问题 3.3 预编译…

07:串口通信(二):收发数据包

1、数据包 我们使用上位机个单片机发送数据包时&#xff0c;规定包头和包尾&#xff0c;将我们需要发送的数据放在中间&#xff0c;数据的长度我们也可以自己规定。一般情况下HEX数据包我们使用固定长度数据包。而文本数据包使用是可变长度数据包。 2、HEX数据包 2.1、HEX固定…

vs2022支持.netframework4.0

下载nuget包 .netframework4.0 解压nuget 复制到C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework 参考 https://www.cnblogs.com/bdqczhl/p/18670152 https://blog.csdn.net/xiaomeng1998_/article/details/135979884

《安富莱嵌入式周报》第350期:Google开源Pebble智能手表,开源模块化机器人平台,开源万用表,支持10GHz HRTIM的单片机,开源CNC控制器

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV1YPKEeyEeM/ 《安富莱嵌入式周报》第350期&#xff1a;Google开…

Oracle临时表空间(基础操作)

临时表空间 临时表空间&#xff1a;用来存放用户的临时数据&#xff0c;临时数据在需要时被覆盖&#xff0c;关闭数据库后自动删除&#xff0c;其中不能存放永久性数据。 用户进程和服务器进程是一对一的叫做专用连接。 任何一个用户连到oracle数据库&#xff0c;oracle都会…

浅识MQ的 Kafka、ActiveMQ、RabbitMQ、RocketMQ区别

DeepSeek回复&#xff1a; 以下是主流消息队列&#xff08;MQ&#xff09;的对比分析&#xff0c;结合核心特性、适用场景和实际案例说明&#xff1a; 一、主流MQ对比分析 维度 Kafka RabbitMQ RocketMQ ActiveMQ所属公司Apache&#xff08;LinkedIn开源&#xff09;…

19.4.2 -19.4.4 新增、修改、删除数据

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 需要北风数据库的请留言自己的信箱。 19.4.2 新增数据 数据库数据的新增、修改和删除不同于查询&#xff0c;查询需要返回一个DbD…

若依系统环境搭建记录

开源若依系统网上资料也很全的&#xff0c;本篇博文记录下自己搭建环境过程中遇到的一些问题。 配置Maven和编辑器选择 我懒得配置Eclipse了&#xff0c;直接用vscode作为编辑器&#xff0c;后面构建运行都用命令行。 配置数据库连接 按照mysql5.7按网上教程即可&#xff1…

Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)

#想cry 好想cry 目录 1 全局唯一id 1.1 自增ID存在的问题 1.2 分布式ID的需求 1.3 分布式ID的实现方式 1.4 自定义分布式ID生成器&#xff08;示例&#xff09; 1.5 总结 2 优惠券秒杀接口实现 3 单体系统下一人多单超卖问题及解决方案 3.1 问题背景 3.2 超卖问题的…

解锁豆瓣高清海报(三)从深度爬虫到URL构造,实现极速下载

脚本地址: 项目地址: Gazer PosterBandit_v2.py 前瞻 之前的 PosterBandit.py 是按照深度爬虫的思路一步步进入海报界面来爬取, 是个值得学习的思路, 但缺点是它爬取慢, 仍然容易碰到豆瓣的 418 错误, 本文也会指出彻底解决旧版 418 错误的方法并提高爬取速度. 现在我将介绍…