【代码质量】RAII在C++编程中的必要性

2023-05-16

文章目录

  • 1 前言
  • 2 什么是RAII
  • 3 为什么用RAII
  • 4 RAII应用
  • 5 小结


1 前言

  C/C++相比其他高级编程语言,具有指针的概念,指针即是内存地址。C/C++可以通过指针来直接访问内存空间,效率上的提升是不言而喻的,是其他高级编程语言不可比拟的;比如访问内存一段数据,通过指针可以直接从内存空间读取数据,避免了中间过程函数压栈、数据拷贝甚至消息传输等等。


  内存交给程序员管理,是存在隐患的;申请了内存,因为意外原因而没有释放,即导致内存泄漏;系统长期运行后因申请不到可用内存而导致异常,甚至崩溃。为了避免内存泄漏,C/C++程序员发明各类方法以检测或者避免内存泄漏。RAII就是一个C++规范标准,遵循该标准,以尽可能避免内存泄漏。


2 什么是RAII

  RAII全称为Resource Acquisition Is Initialization,由C++发明者Bjarne Stroustrup提出的设计理念;中文可直译为资源获取即为初始化,是C++语言的一种管理资源、避免泄漏的方法标准。RAII基本原则是,资源与对象的生命周期绑定,利用C++类将由程序员管理的资源间接转换为由系统管理,程序员不需显式地释放资源,以从根本上避免内存泄漏,基本步骤包括:

  • 自动申请资源
  • 使用资源
  • 自动释放资源

  RAII实现过程易于理解,在创建一个对象是,构造函数用于申请资源空间,在对象生命周期内,资源可用正常访问;对象超出作用域或者生命周期结束后(释放),系统调用析构函数释放已申请的资源。


3 为什么用RAII

  • 将由程序员管理的资源转换为由系统管理
  • 避免内存泄漏
  • 良好的编程约束标准

4 RAII应用

  RAII典型的应用例子就是C++11引入的智能指针、类模板锁,以解决内存泄漏、死锁问题。

  • 智能指针
  • 类模板lock_guard

  以一个“申请—释放”内存的最常用过程为例:

  • 常规写法,确保函数所有出口都释放申请的内存
int main(int argc, char * * argv)
{ 
    int *p = NULL;
    bool condition0 = false;
    bool condition1 = false; 
    
    p = new int();

    /* todo */

    if (condition0)
    {
        delete p;
        return -1;    
    }

    if (condition1)
    {
        delete p;
        return -1; 
    }

   delete p;

   return 0;
}

  • 因为中途退出函数,(忘记)未释放内存,导致内存泄漏
int main(int argc, char * * argv)
{ 
    int *p = NULL;
    bool condition0 = false;
    bool condition1 = false;
        
    p = new int();

    /* todo */

    if (condition0)
    {
        return -1;     /* 可能导致内存泄漏 */
    }

    if (condition1)
    {
        return -1 ;     /* 可能导致内存泄漏 */
    }
    
    delete p;

    return 0;
}

  • 引入RAII类,将内存交给系统管理
class new_raii
{  
public:  
    explicit new_raii(std::function<void()> fun):delete_fun(fun)
    {
        std::cout << "call constrcutor fun" << std::endl;
    }
    
    ~new_raii() 
    { 
        std::cout << "call destrcutor fun" << std::endl;
        delete_fun();   /* 调用释放资源函数 */
    }
private:  
    std::function<void()> delete_fun; 
}; 

  • RAII完整示例
#include <iostream>
#include <functional>

class new_raii
{  
public:  
    explicit new_raii(std::function<void()> fun):delete_fun(fun)
    {
        std::cout << "call constrcutor fun" << std::endl;
    }
    
    ~new_raii() 
    { 
        std::cout << "call destrcutor fun" << std::endl;
        delete_fun();   /* 调用释放资源函数 */
    }
private:  
    std::function<void()> delete_fun; 
}; 

int main(int argc, char * * argv)
{ 
    int *p = NULL;
    bool condition0 = true;
    bool condition1 = false; 
    
    p = new int();

	/* 将申请内存交给raii类对象管理 */
    new_raii([&]
        {
            std::cout << "call delete fun" << std::endl;
            delete p;
        }	/* 指定删除资源函数 */
    );	
    
    /* todo */
    if (condition0)
    {
        return -1;    
    }

    if (condition1)
    {
        return -1; 
    }

   return 0;
}

  编译执行结果:

acuity@ubuntu:/home/RAII$ g++ raii.cpp -o raii -std=c++11
acuity@ubuntu:/home/RAII$ ./raii
call constrcutor fun
call destrcutor fun
call delete fun
acuity@ubuntu:/home/RAII$ 

  从结果看,无论函数何时退出,都无需显示调用delete释放申请的内存。只要类对象生命周期结束,即调用析构函数释放内存。


5 小结

  RAII本质是将资源和对象生命周期绑定,将资源管理任务转化为对象管理任务,资源的申请和释放由系统自动调用构造和析构函数实现;将程序员管理的资源间接转为由系统管理。

  内存是系统资源之一,除此之外,其他系统资源如申请了,没有及时释放,同样会导致“资源泄漏”。对于其他系统资源,也可以参考RAII原则,确保系统的稳定性。内存是最直接接触的资源,不限于内存,常用系统资源及异常现象包括:

  • 文件描述符fd
      open了文件描述符,没有及时close,导致系统文件描述符用尽。

  • 互斥锁
      互斥锁资源的使用不当,导致死锁产生。

  • socket套接字
      socket套接字用尽,导致创建socket失败。

  • 端口、进程、线程、文件等等有限的资源

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【代码质量】RAII在C++编程中的必要性 的相关文章

  • 无人机地面站VS版

    界面如图所示 xff0c 源代码如下 源代码
  • 无人机地面站QT版

    下载链接 源代码
  • 利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来

    span class hljs keyword void span func span class hljs keyword int span n span class hljs keyword void span main span cl
  • 低通滤波(一阶)

    1 一阶滤波算法的原理 一阶滤波 xff0c 又叫一阶惯性滤波 xff0c 或一阶低通滤波 是使用软件编程实现普通硬件RC低通滤波器的功能 一阶低通滤波的算法公式为 xff1a Y n 61 1 Y n 1 43 X n 式中 xff1a
  • I2C初始化

    一 管脚初始化 由于STM32的硬件初始化比较复杂 xff0c 这里我们采用软件初始化 xff0c 选用端口PB6 PB7 以匿名的I2C初始化程序为例 span class hljs comment I2C GPIO定义 span spa
  • 四轴思路(持续更新)

    遥控器 xff1a 发送数据 MPU6050 xff1a 加速度做一维卡尔曼滤波 xff0c 角速度做一介低通滤波 然后四元数姿态解算 接着串级PID控制平衡 xff0c 外环角度环 xff0c 内环角速度环 高度控制 xff1a 高度和速
  • QT 上位机(网络通信)

    Client类 span class hljs comment span class hljs xmlDocTag span span class hljs xmlDocTag span span class hljs xmlDocTag
  • win10系统如何通过ssh远程登录另一台电脑的deepin linux系统

    1 首先 xff0c 更新一下软件源 xff0c 打开 终端窗口 xff0c 输入 sudo apt get update 2 然后 xff0c 在终端中 xff0c 输入 sudo apt get install openssh serv
  • 基于STM32与ESP8266的太空人WiFi天气时钟(代码开源)

    前言 xff1a 本文为手把手教学ESP8266著名开源项目 太空人WiFi天气时钟 xff0c 不同的是本次项目采用的是STM32 作为MCU 两者开发过程中有因为各自芯片的特点 xff08 时钟频率 xff0c 内存大小等 xff09
  • make menuconfig错误的解决办法

    如果使用make menuconfig的方式配置内核 xff0c 又碰巧系统没有安装ncurses库 xff08 ubuntu系统 默认就没有安装此库 xff09 xff0c 就会出现错误 xff0c 错误信息大体上如下 xff1a Una
  • FileZilla以root用户登录Linux

    一 首先创建root用户的密码 span class hljs built in sudo span passwd root 然后输入要设置的密码 xff0c 然后再输入一次 xff0c 成功 xff01 二 修改配置文件 filezila
  • px4编译

    如果下载速度特别慢 xff0c 可以使用手机的4G网络 位置确定 mkdir p src cd src 开始下载指定版本的px4 xff0c 在这里是v1 8 2版本 git clone b v1 8 2 https github com
  • px4源码----位置估算(position_estimator_inav_params.h)

    pragma once include lt parameters param h gt struct position estimator inav params float w z baro 权重 z轴 气压计位置 0 5 float
  • ubuntu如何把调整cpu策略

    一 安装cpu频率管理软件 sudo apt get install cpufrequtils 二 查看cpu当前状态 cpufreq info 其中available cpufreq governors xff1a performance
  • 树莓派系统介绍

    树莓派是一个微型计算机 xff0c 和普通的电脑没有什么区别 xff0c 只是体积更小 xff0c 只有卡片大小 xff0c 存储能力和计算能力会差一点 xff0c 主要用于学习 xff0c 实验所用 是电脑就要安装操作系统 xff0c 树
  • 小觅摄像头ROS编译错误

    GitHub slightech MYNT EYE ORB SLAM2 Sample Forked from ORB SLAM2 https github com raulmur ORB SLAM2 Forked from ORB SLAM
  • 华为路由器交换机常用命令(随时补充更新)

    一 视图切换 lt huawei gt 用户视图 huawei 系统视图 xff0c 在用户视图状态下输入sys进入 xff0c 在系统视图下输入quit或者return返回用户视图 huawei g0 0 1 端口视图 xff0c 从系统
  • 01路径规划问题的相关理论

    目录 1 旅行商问题 2 有能力约束的车辆路径问题 3 车辆路径主要要素特征 4 约束条件分析 5 带时间窗的车辆路径问题 6 车辆路径问题求解算法 7 小节 1 旅行商问题 旅行商问题 xff08 Traveling Saleman Pr
  • 时序数据库-3-[IoTDB]的安装与使用

    IoTDB官方文档手册 Apache IoTDB xff08 物联网数据库 xff09 是一体化收集 存储 管理与分析物联网时序数据的软件系统 Apache IoTDB 采用轻量式架构 xff0c 具有高性能和丰富的功能 xff0c 并与A
  • 【强烈推荐】基于STM32的TFT-LCD各种显示实现(内容详尽含代码)

    前言 xff1a TFT LCD模块作为人们日常生活中常见屏幕类型之一 xff0c 使用的受众面非常广阔 例如 xff1a 显示各个传感器数值 xff0c 显示精美界面 xff0c 多级化菜单系统等等都不离不开他的身影 可以说学会TFT L

随机推荐

  • 时序数据库-4-[IoTDB]的python3操作

    从采集到存储 xff1a 时序数据库到底怎么处理时间 xff1f iotdb官方文档手册 1 容器安装iotdb 可以使用docker volume create命令创建 docker 卷 此命令将在 var lib docker volu
  • [汇总]基于ESP32的四旋翼无人机开发纪实

    文章目录 一 项目说明1 已实现功能2 硬件配置 二 ESPlane2 0 开发笔记三 相关传感器驱动移植四 参考链接 ESPlane 项目更名为 ESP Drone 现已公开代码仓库和文档 代码仓库 xff1a https github
  • [填坑]Ubuntu安装显卡专有驱动后鼠标键盘无法使用

    问题描述 我在两个地方遇到了同样的问题 xff0c 解决方法也如出一辙 xff0c 由于没有研究源码 xff0c 暂不清楚原因 问题1描述 xff1a 为了解决Ubuntu下笔记本功耗问题 xff0c 在网友建议下我安装了bumblebee
  • uniapp-前后端开发app-系列01开篇

    系列文章目录 文章目录 系列文章目录前言一 开发工具 xff1f 二 项目架构三 具体内容实现 前言 提示 xff1a 这里可以添加本文要记录的大概内容 xff1a 随着app和小程序的发展 有没有开发一个模版 其他端程序都能用 uniap
  • TypeError: iter() returned non-iterator of type

    在使用Python迭代器时出现错误 xff1a class Fibs def init self self a 61 0 self b 61 1 def next self self a self b 61 self b self a 43
  • 【Linux应用编程】一个异步信号处理引起死锁问题的思考

    文章目录 1 前言2 为什么会产生死锁2 1 死锁2 2 分析2 3 结论 3 避免死锁4 举一反三5 死锁例子代码6 参考文章 1 前言 最近在维护别人的代码时 xff0c 遇到一个线程死锁问题 xff0c 一番折腾 xff0c 最终定位
  • 【RT-Thread】SGM706独立看门狗软件包

    文章目录 1 简介1 1 目录结构1 2 许可证 2 芯片介绍3 支持情况4 使用说明4 1 依赖4 2 获取软件包4 3 初始化4 4 启动看门狗4 5 msh finsh测试查看设备注册通过msh启动看门狗 5 注意事项6 联系方式 1
  • 利用tldr工具再也不怕记不住Linux命令

    文章目录 1 前言2 tldr3 安装4 使用 1 前言 linux命令非常多 xff0c 少用的命令往往易忘记 xff0c 甚至常用的语法较为复杂的命令也不好记住 当然有些太复杂的命令也不需要死记硬背 xff0c 我们往往会借助man命令
  • C++中的二阶构造函数

    文章目录 1 前言2 二阶构造3 总结 1 前言 构造函数用于创建对象时对象成员的初始化 xff0c 如赋初值 申请内存 加载文件等 xff0c 即是自动完成对象的初始化任务 在C 43 43 语言中 xff0c 构造函数执行顺序是 xff
  • open函数簇与fopen函数簇区别和用法

    文章目录 1 前言2 open与fopen区别2 1 标准不同2 2 层次不同2 3 适用对象不同 xff08 返回值不同 xff09 2 4 缓冲区2 5 效率不同 3 使用方法3 1 open3 2 fopen 1 前言 linux系统
  • 基于STM32的OLED多级菜单GUI实现(简化版智能手表)

    前言 xff1a 本文的OLED多级菜单UI 为一个综合性的STM32小项目 xff0c 使用多传感器 与OLED显示屏 实现智能终端 的效果 项目中的多级菜单UI使用了较为常见的结构体索引法 去实现功能与功能之间的来回切换 xff0c 搭
  • 【RTD】铂电阻测温原理与具体方法

    文章目录 1 基本原理2 铂电阻2 1 铂电阻测温原理2 2 铂电阻类型和测量方法2 2 1 两线式铂电阻2 2 2 三线式铂电阻2 2 3 四线式铂电阻 3 小结 相关文章 xff1a RTD 铂电阻测温原理与具体方法 RTD AD779
  • 【RTD】AD7793三线式铂电阻PT100/PT1000应用

    文章目录 1 AD7793简介2 AD7793 三线式铂电阻测量2 1 阻值计算 3 小结 相关文章 xff1a RTD 铂电阻测温原理与具体方法 RTD AD7793三线式铂电阻PT100 PT1000应用 RTD AD7793四线式铂电
  • 【RTD】AD7793四线式铂电阻PT100/PT1000应用

    文章目录 1 前言2 AD7793 四线式铂电阻测量2 1 阻值计算 3 小结 1 前言 上一篇文章描述的是RTD驱动芯片AD7793特点 xff0c 以及其与三线式RTD连接使用方法 本文描述四线式RTD与AD7793的使用 相关文章 x
  • 【RTD】AD7793两线式铂电阻PT100/PT1000应用

    文章目录 1 前言2 AD7793 两线式铂电阻测量2 1 阻值计算 3 小结 1 前言 上一篇文章描述的是RTD驱动芯片AD7793与四线式RTD连接使用方法 本文描述两线式RTD与AD7793的使用 相关文章 xff1a RTD 铂电阻
  • 【RTD】AD7793驱动程序

    文章目录 1 前言2 AD7793驱动程序2 1 spi访问接口2 2 寄存器和常用配置值2 3 初始化2 4 原始数据获取2 5 阻值换算 3 使用4 完整工程代码 1 前言 前面文章主要描述AD7793分别与两线 三线 四线RTD连接电
  • 【RTD】二分法查找和分段线性插值算法在RTD中应用

    文章目录 1 前言2 二分法查找2 1 复杂度2 2 实现 3 分段线性插值4 RTD实例 1 前言 处理器通过RTD采集电路 xff08 芯片 xff09 精确获得当前RTD电阻值后 xff0c 再结合RTD与温度线性关系表 xff0c
  • 24系列EEPROM/FRAM通用驱动库移植到RT-Thread

    文章目录 1 前言2 接口实现2 1 i2c收发函数实现2 2 页写延时函数2 3 写保护函数2 4 设备注册 3 对接RT Thread设备驱动3 1 标准设备驱动接口3 2 注册到RT Thread3 3 导出到msh3 4 测试 4
  • 【RT-Thread】TCA9534 8位I/O扩展器驱动软件包

    文章目录 1 简介1 1 目录结构1 2 许可证 2 芯片介绍3 支持情况4 使用说明4 1 依赖4 2 获取软件包4 3 初始化4 4 访问设备4 5 msh finsh测试查看设备注册执行sample 5 代码仓库 1 简介 tca95
  • 【代码质量】RAII在C++编程中的必要性

    文章目录 1 前言2 什么是RAII3 为什么用RAII4 RAII应用5 小结 1 前言 C C 43 43 相比其他高级编程语言 xff0c 具有指针的概念 xff0c 指针即是内存地址 C C 43 43 可以通过指针来直接访问内存空