C++惯用法之RAII思想: 资源管理

C++编程技巧专栏:http://t.csdnimg.cn/eolY7

相关系列文章

C++智能指针的自定义销毁器(销毁策略)

目录

1.概述

 2.RAII的应用

2.1.智能指针

2.2.文件句柄管理

2.3.互斥锁

3.注意事项

3.1.禁止复制

3.2.对底层资源使用引用计数法

3.3.复制底部资源(深拷贝)或者转移资源管理权(移动语义)

4.RAII的优势和挑战

5.总结


1.概述

        RAII是Resource Acquisition Is Initialization的缩写,即“资源获取即初始化”。RAII原则的基本思想是将资源的生命周期与对象的生命周期绑定在一起。它是C++语言的一种管理资源、避免资源泄漏的惯用法,利用栈的特点来实现,这一概念最早由Bjarne Stroustrup提出。在函数中由栈管理的临时对象,在函数结束时会自动析构,从而自动释放资源,因此,我们可以通过构造函数获取资源,通过析构函数释放资源。这种自动管理资源的方式可以大大减少资源泄漏、野指针和其他与资源管理相关的问题。常见的写法为:

Object() {// acquire resource in constructor
}
~Object() {// release resource in destructor
}

 2.RAII的应用

2.1.智能指针

智能指针是RAII原则在内存管理中的一个典型应用。C++11引入了多种智能指针类型,如std::unique_ptr、std::shared_ptr和std::weak_ptr,它们可以自动管理动态分配的内存。

例如,使用std::unique_ptr可以确保在不需要动态分配的内存时自动释放它:

#include <iostream>
#include <memory>class MyClass {
public:MyClass() { std::cout << "MyClass created\n"; }~MyClass() { std::cout << "MyClass destroyed\n"; }
};int main() {{std::unique_ptr<MyClass> ptr(new MyClass()); // MyClass对象被创建// 当ptr离开这个作用域时,它会自动释放所指向的MyClass对象} // MyClass对象在这里被销毁,输出"MyClass destroyed"return 0;
}

在这个例子中,当ptr离开其作用域时,std::unique_ptr的析构函数会被调用,从而释放它所指向的MyClass对象。这种自动的内存管理方式避免了手动调用delete可能导致的错误。

2.2.文件句柄管理

另一个常见的应用是使用RAII原则管理文件句柄。通过创建一个封装了文件句柄的类,可以确保在不需要文件时自动关闭它。

例如:

#include <fstream>
#include <iostream>class FileWrapper {
public:FileWrapper(const std::string& filename, std::ios_base::openmode mode): file_(filename, mode) {if (!file_.is_open()) {throw std::runtime_error("无法打开文件: " + filename);}}~FileWrapper() {file_.close(); // 在析构函数中关闭文件句柄}// 提供对内部文件的访问(如果需要的话)std::fstream& file() { return file_; }private:std::fstream file_; // 封装文件句柄的成员变量
};

在这个例子中,FileWrapper类的构造函数打开一个文件,并在析构函数中关闭它。这确保了即使在异常情况下,文件句柄也会被正确关闭。

2.3.互斥锁

在多线程编程中,std::lock_guard, std::unique_lock, std::shared_lock等也利用了RAII的原理,用于管理互斥锁。当这些类的等对象创建时,会自动获取互斥锁;当对象销毁时,会自动释放互斥锁。

std::lock_guard的构造函数如下:

template< class Mutex > class lock_guard;

std::lock_guard的析构函数会自动释放互斥锁,因此,我们可以通过std::lock_guard来管理互斥锁,从而避免忘记释放互斥锁。如:

std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); // unlock when lock is out of scope

不使用RAII的情况下,我们需要手动释放互斥锁,如下所示:

std::mutex mtx;
mtx.lock();
// ...
mtx.unlock();

3.注意事项

在资源管理类中小心copy行为

  • 拷贝RAII对象必须考虑其管理的资源,针对其资源做出拷贝行为的实现
  • 常见的RAII对象拷贝行为:拒绝拷贝、引用计数法、深拷贝、资源所有权转移

并非所有资源都是基于堆的(heap-based),对于这种对象不能直接使用智能指针,需要自定义其资源管理类。例如:为了说明锁的资源管理行为,我们这里给定义一个锁,来替代C++里的锁

struct MyMutex {MyMutex() {printf("Construct MyMutex\n");}~MyMutex() {printf("Deconstruct MyMutex\n");}
};

其上锁解锁行为:

void lock(MyMutex *) {printf("lock\n");
}void unlock(MyMutex *) {printf("unlock\n");
}

锁的资源管理类,在构造函数获取资源(加锁),在析构函数释放资源(解锁):

struct Lock {
private:MyMutex *myMutex;
public:explicit Lock(MyMutex *mutex) : myMutex(mutex) {lock(myMutex);}~Lock() {unlock(myMutex);}
};

使用:

int main() {MyMutex myMutex;{printf("---------\n");Lock lk(&myMutex);printf("---------\n");// 离开代码块将自动析构局部对象,因此会释放锁}
}
/*
Construct MyMutex
---------
lock
---------
unlock
Deconstruct MyMutex
*/

潜在风险,如果发生了拷贝行为:

Lock l1(&mutex);
Lock l2(l1);

那么将立即死锁(Linux里一般是非递归锁,重复加锁会造成死锁)

3.1.禁止复制

继承nocopyable,或者将拷贝相关函数设置为delete。如:

//[1]
class NonCopyable
{
protected:NonCopyable(const NonCopyable&){}NonCopyable& operator=(NonCopyable&){}
};或//[2]
class NonCopyable
{
public:NonCopyable(const NonCopyable&)=delete;NonCopyable& operator=(const NonCopyable&)=delete;
};

3.2.对底层资源使用引用计数法

思想:维护一个计数器,当最后一个使用者被销毁时,才真正释放资源,如:

struct Lock {
private:shared_ptr<MyMutex> mutexPtr;
public:// 将unlock函数设置为删除器explicit Lock(MyMutex *mutex) : mutexPtr(mutex, unlock) {lock(mutexPtr.get());}// 不必声明析构函数,因为mutexPtr是栈上对象,所以会被默认释放,那么智能指针就会调用其释放器unlock
};

3.3.复制底部资源(深拷贝)或者转移资源管理权(移动语义)

在资源管理类中提供对原始资源的访问

  • API常需要要求访问原始资源,所以RAII资源管理类应该提供访问原始资源的接口
  • 对原始资源可以由显示转换或者隐式转换获得.其在安全性和方便性上各有取舍

智能指针提供了get接口来访问原始资源

在其中要注意,不可以get一个智能指针去初始化另一个智能指针,否则会发生重复释放

int main() {shared_ptr<MyMutex> p1 = make_shared<MyMutex>();{shared_ptr<MyMutex> p2(p1.get());cout << p1.use_count() << " " << p2.use_count() << endl;
//        1 1
//        p2离开代码块,释放其管理的资源,p1指针指向被释放的内存}
}

程序将异常退出

4.RAII的优势和挑战

优势:

  1. 自动资源管理:通过绑定资源的生命周期与对象的生命周期,RAII自动处理资源的获取和释放,减少了手动管理的错误。

  2. 代码简洁性:RAII原则鼓励将资源管理逻辑封装在类中,使代码更加清晰和易于维护。

  3. 异常安全性:当使用RAII时,即使在异常情况下,资源也会被正确释放,这有助于提高程序的健壮性。

挑战:

  1. 资源所有权的转移:在使用RAII时,需要仔细考虑资源所有权的转移。例如,在使用智能指针时,需要明确何时使用std::move来转移所有权。

  2. 与旧代码的兼容性:在将RAII原则应用于现有代码库时,可能需要大量的重构工作来适应新的资源管理方式。

  3. 学习曲线:对于初学者来说,理解和正确应用RAII原则可能需要一些时间和经验。

5.总结

        RAII原则为C++程序员提供了一种强大且优雅的资源管理方法。通过将资源的生命周期与对象的生命周期绑定在一起,RAII不仅简化了资源管理,还提高了代码的健壮性和可维护性。然而,为了充分利用RAII的优势,程序员需要仔细设计类的接口和实现,并考虑到资源所有权和资源转移的问题。

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

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

相关文章

2024新版微信小程序登录注册功能的实现,授权登录,退出,缓存讲解,小程序个人中心的实现,修改头像 图片上传功能的实现 新版登陆注册,头像上传,修改昵称

新版小程序授权登录注册获取头像昵称文档 一&#xff0c;无法获取用户的微信头像和昵称 最近好多同学在学习石头哥小程序课程的时候&#xff0c;遇到了下面这样的问题&#xff0c;在小程序授权获取用户头像和昵称时&#xff0c;获取到的是下面这样的。 到底是什么原因导致的…

YOLO v1讲解

YOLO是最经典的一阶目标检测框架&#xff0c;记录一下v1思路。 整体流程 输入数据一张 448 448 3 448 \times 448 \times 3 4484483 的图片&#xff0c;切分成 7 7 7 \times 7 77 的网格将图片经过多层CNN&#xff0c;下采样得到 7 7 30 7 \times 7 \times 30 7730 的f…

代码随想录算法训练营第day9|28. 找出字符串中第一个匹配项的下标、459.重复的子字符串

a.28. 找出字符串中第一个匹配项的下标 题目链接 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示…

关于JVM的小总结(待补充)

JVM组成及他们之间的关系 装载类子系统字节码执行引擎运行时数据区 装载类子系统 类加载器字节码调节器类加载运行时数据区 字节码执行引擎 运行时数据区 线程私有 虚拟机栈本地方法栈程序计数器 线程共享 堆方法区&#xff08;元空间&#xff09;

Vue3 中的代理原理详解

Vue3 中的代理原理详解 Vue3 中引入了代理&#xff08;Proxy&#xff09;机制&#xff0c;取代了 Vue2 中的 Object.defineProperty() 机制&#xff0c;用于实现数据响应式。代理机制是 ES6 中新增的特性&#xff0c;它可以用来自定义对象中的操作&#xff0c;比如属性查找、赋…

rabbitmq总结

一、初次感知 https://www.cnblogs.com/zqyx/p/13170881.html 这篇文章非常好&#xff0c;讲了一些持久化的原理。 1. 第一次使用rabbitmq发信息 // 创建连接工厂ConnectionFactory connectionFactorynew ConnectionFactory();connectionFactory.setHost("192.168.88.1…

php使用ElasticSearch

ElasticSearch简介 Elasticsearch 是一个分布式的、开源的搜索分析引擎&#xff0c;支持各种数据类型&#xff0c;包括文本、数字、地理、结构化、非结构化。 Lucene与ElasticSearch Apache Lucene是一款高性能的、可扩展的信息检索&#xff08;IR&#xff09;工具库&#xf…

Qt添加VTK并绘制图形

文章目录 准备环境使用VS创建Qt Widget项目配置VTK依赖调试C/C链接器 添加vtk窗口测试代码 参考链接&#xff1a; VS2017配置QT环境(详细版)_vs2017 qt-CSDN博客 QT5VTK9.1最新配置方法_qt vtk-CSDN博客 VTK笔记-Qt5.12.11编译VTK9.0.3-QVTKOpenGLNativeWidget-CSDN博客 准…

Java二级--操作题详解(1)

目录 1.第一套&#xff1a; 1.1 基本操作&#xff1a; 1.2 题解分析&#xff1a; 2.1 简单应用&#xff1a; 2.2 解题分析&#xff1a; 3.1 综合应用&#xff1a; 3.2解题分析&#xff1a; 1.第一套&#xff1a; 1.1 基本操作&#xff1a; 在考生文件夹中存有文件名为J…

Leetcode HOT150

55. 跳跃游戏 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1 …

C++ · 代码笔记3 · 引用

目录 前言011引用初探_引用与普通变量012引用初探_引用作为函数参数013引用初探_引用作为函数返回值014引用初探_引用返回局部函数造成的错误015引用初探_多级引用020引用与指针递增的区别030const与引用040使用const限定的函数形参引用 前言 本笔记所涉及到的编程环境与 《C …

怎么对接迅雷网盘拉新项目?迅雷网盘怎么做才有效果?

自网盘拉新项目上线以来&#xff0c;网盘市场日益繁荣&#xff0c;各大厂商纷纷进军这一领域。头条网盘、悟空网盘、UC网盘、迅雷网盘等都成为了各个推广达人喜欢的推广项目。其中&#xff0c;迅雷网盘凭借其稳定的服务、强大的功能和广泛的用户基础&#xff0c;成为了市场中的…

西门子S120故障报警F30003的解决办法总结

西门子S120故障报警F30003的解决办法总结 如下图所示&#xff0c;压机在回程时突然出现报警&#xff0c;故障代码为&#xff1a;30003&#xff0c; 如下图所示&#xff0c;查找手册可以看到F30003的报警分析为&#xff1a;直流母线欠压 如下图所示&#xff0c;本来想测量输入端…

三八妇女节智慧花店/自动售花机远程视频智能监控解决方案

一、项目背景 国家统计局发布的2023年中国经济年报显示&#xff0c;全年社会消费品零售总额471495亿元&#xff0c;比上年增长7.2%。我国无人零售整体发展迅速&#xff0c;2014年市场规模约为17亿元。无人零售自助终端设备市场规模超过500亿元&#xff0c;年均复合增长率超50%。…

如何阅读“计算机界三大神书”之一 ——《计算机程序的构造和解释》SICP

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

总结Redis的原理

一、为什么要使用Redis 缓解数据库访问压力mysql读请求进行磁盘I/O速度慢&#xff0c;给数据库加Redis缓存&#xff08;参考CPU缓存&#xff09;&#xff0c;将数据缓存在内存中&#xff0c;省略了I/O操作 二、Redis数据管理 2.1 redis数据的删除 定时删除惰性删除内存淘汰…

NHANES数据(复杂调查数据)亚组交互函数1.7(P for interaction)发布-纠正了目前的一个问题

大家好&#xff0c;有粉丝私信我说NHANES数据(复杂调查数据)亚组交互函数1.版本交互函数有点问题&#xff0c;我查看了一下&#xff0c;有个代码调用失效了。就是下面这个&#xff0c;本来我是这样调用数据的 ids<-match.call()$ids应该是由于R版本或者survy包升级后导致这…

基于sprinbgoot的火锅店管理系统(程序+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

从新能源汽车行业自动驾驶技术去看AI的发展未来趋势

自动驾驶汽车关键技术主要包括环境感知、精准定位、决策与规划、控制与执行、高精地图与车联网V2X以及自动驾驶汽车测试与验证技术等。 &#x1f413; 自动驾驶技术 这是AI在汽车行业中应用最广泛的领域之一。自动驾驶技术利用AI算法和传感器来感知环境、识别障碍物&#xff0c…

mysql的语法总结2

命令&#xff1a; mysql -u 用户名 -p mysql登录 命令&#xff1a;create database u1 创建数据库u1 查询数据库 使用数据库u1 创建表department 查询表department ALTER TABLE 表名 操作类型&#xff1b; 操作类型可以有以下的操作&#xff1a; 添加列&#x…