当前位置: 首页 > news >正文

【一篇即毕业系列】RAII管理从基础到通天!!看这一篇就够了!!

文章目录

  • RAII 编程思想
    • 什么是 RAII
    • RAII 的应用
    • RAII的巧用
    • ScopeExit实现
  • 统计函数耗时

RAII 编程思想

RAII 在 C++ 中是个很常见的也是很重要的编程思想。

什么是 RAII

Resource Acquisition Is Initialization,资源获取即初始化,将资源的生命周期与一个对象的生命周期绑定,举例来说就是,把一些资源封装在类中,在构造函数请求资源,在析构函数中释放资源且绝不抛出异常,而一个对象在生命周期结束时会自动调用析构函数,即资源的生命周期与一个对象的生命周期绑定。

RAII 的应用

见如下代码:

std::mutex mutex;voidfunc(){}voidNoRAII(){mutex.lock();func();if(xxx){mutex.unlock();// 多次需要调用unlock(),还有可能忘记调用unlock导致一直持有锁return;}...mutex.unlock();}voidRAII(){//不需要显式调用unLockstd::lock_guard<std::mutex>lock(mutex);func();if(xxx){return;}...return;}

RAII 的应用非常多,C++ 的 STL 基本都遵循 RAII 规范,典型的如vectorstringlock_guardunique_lockshared_ptrunique_ptr等。

RAII的巧用

boost 中有个ScopeExit,这是个很高级的特性,利用 RAII 特性,可以在作用域结束时自动关闭已经打开的资源或做某些清理操作,类似于unique_ptr,但又比unique_ptr方便,不需要自定义delete函数。

举例: 如果没有ScopeExit

voidtest(){char*test=newchar[100];if(a){delete[]test;// count 1return;}xxx;if(b){delete[]test;// count 2return;}...delete[]test;// count 3}

使用了ScopeExit

voidtest(){char*test=newchar[100];std::ofstreamofs("test.txt");ScopeExit{delete[]test;// 在test函数生命周期结束后自动执行delete[]操作ofs.close();// 在生命周期结束后自动关闭文件,这里只是举个不恰当例子,ofstream自动生命周期结束后就会关闭};if(a){return;}xxx;if(b){return;}...}

当然,正常 C++ 代码鼓励使用裸指针,可以使用智能指针来申请资源,这里只是举个例子,使用ScopeExit也可以用于处理文件资源的关闭等等。

两者代码比较后优劣程度显而易见,不使用ScopeExit需要在return前多次做资源清理操作,而使用了ScopeExit则只需做一次声明后在作用域结束后会自动进行相关的资源清理操作,方便而且不易出错。

ScopeExit实现

这里参考 boost 使用 C++11 实现了一套 ScopeExit 机制

classScopeExit{public:ScopeExit()=default;ScopeExit(constScopeExit&)=delete;voidoperator=(constScopeExit&)=delete;ScopeExit(ScopeExit&&)=default;ScopeExit&operator=(ScopeExit&&)=default;template<typenameF,typename...Args>ScopeExit(F&&f,Args&&...args){func_=std::bind(std::forward<F>(f),std::forward<Args>(args)...);}~ScopeExit(){if(func_){func_();}};private:std::function<void()>func_;};#define_CONCAT(a,b)a##b#define_MAKE_SCOPE_(line)ScopeExit_CONCAT(defer,line)=[&]()#undefSCOPE_GUARD#defineSCOPE_GUARD_MAKE_SCOPE_(__LINE__)

使用方式如下:

voidtest(){char*test=newchar[100];std::ofstreamofs("test.txt");SCOPE_GUARD{delete[]test;ofs.close();};// 上面这段代码等同于ScopeExit defer10=[&](){delete[]test;ofs.close();};// 触发 ScopeExit(F&& f, Args&&... args)的构造函数// [&]() { ... }被封装成 ScopeExit中的func_,析构函数时候触发if(a){return;}...if(b){return;}...}

RAII 还有很多有趣的妙用,我们可以使用 RAII 方式来更方便的打印函数耗时。

统计函数耗时

我们平时编程过程中不可避免的需要考虑程序的性能,其中,最主要的也是最常见的性能就是"函数耗时",基本上每个开发者都有打印某个函数耗时的需求,你平时打印函数时间是否使用的是如下方式:

voidFunc(){...}intCalTime(){intbegin=GetCurrentTime();// 伪代码Func();intend=GetCurrentTime();cout<<"func time is "<<end-begin<<" s"<<endl;}

计算某个函数的耗时就在函数前后获取时间之后再算差值,丑,麻烦。

这里还可以利用 RAII 方式,把函数的生命周期和一个对象绑定,对象创建时候执行函数,对象生命周期结束析构时候函数执行完毕,这样对象存活的时间就是函数的耗时,见代码:

#pragmaonce#include<sys/time.h>#include<chrono>#include<ctime>#include<fstream>#include<iostream>#include<string>usingllong=longlong;usingnamespacestd::chrono;usingstd::cout;usingstd::endl;namespacetimer{classTimerLog{public:TimerLog(conststd::string tag){//对象构造时候保存开始时间m_begin_=high_resolution_clock::now();m_tag_=tag;}voidReset(){m_begin_=high_resolution_clock::now();}llongElapsed(){returnstatic_cast<llong>(duration_cast<std::chrono::milliseconds>(high_resolution_clock::now()-m_begin_).count());}~TimerLog(){// 对象析构时候计算当前时间与对象构造时候的时间差就是对象存活的时间autotime=duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()-m_begin_).count();std::cout<<"time { "<<m_tag_<<" } "<<static_cast<double>(time)<<" ms"<<std::endl;}private:std::chrono::time_point<std::chrono::high_resolution_clock>m_begin_;std::string m_tag_;};}

使用方式:

voidTestTimerLog(){autofunc=[](){for(inti=0;i<5;++i){cout<<"i "<<i<<endl;std::this_thread::sleep_for(std::chrono::milliseconds(1));}};{timer::TimerLogt("func");func();}}

程序输出:

i 0 i 1 i 2 i 3 i 4 time { func } 5 ms

这样就可以很方便的打印函数时间,但是这里每次都需要定义一个对象,貌似也不太方便,可以考虑加个宏,如下:

#defineCAL_SCOPE_TIME(x)timer::TimerLogt(x)

再如下使用:

voidTestTimerLog(){autofunc=[](){for(inti=0;i<5;++i){cout<<"i "<<i<<endl;std::this_thread::sleep_for(std::chrono::milliseconds(1));}};{CAL_SCOPE_TIME("func time");func();}}
http://www.jsqmd.com/news/525341/

相关文章:

  • 1258:【例9.2】数字金字塔 回溯搜索(超时)解法示例
  • Comsol 中的随机激光:奇妙的微观能量之旅
  • 2026高阻燃热缩管优质供应商推荐指南:PVDF热缩套管/PVDF热缩管/密封防水热缩套管/密封防水热缩管/异形热缩套管/选择指南 - 优质品牌商家
  • Cursor配置GitHub MCP Server避坑指南:个人访问令牌(PAT)的正确生成与安全使用
  • HY-Motion 1.0实战:用一句话生成虚拟偶像跳舞动作
  • 风光储三相PQ并网系统实战手记
  • SAP 批量处理分包事后调整:BAPI_GOODSMVT_CREATE 关键参数与避坑指南
  • translategemma-4b-it效果实测:Ollama环境下对模糊/低清/倾斜图片的鲁棒性翻译表现
  • 如何快速构建黑苹果EFI:OpCore Simplify自动化配置指南
  • Claude Code配置和使用 - fx
  • Rust的匹配中的通配符模式与变量绑定在模式忽略中的语义区别
  • 3步掌握BongoCat:让呆萌猫咪陪你敲代码的桌面互动新体验
  • 乙巳马年·皇城大门春联生成终端W结合Dify:零代码构建春联AI应用
  • lcdgfx嵌入式图形库:轻量双缓冲与跨平台显示驱动
  • 让Windows 7焕发新生:PythonVista项目为你提供现代Python支持
  • 大厂泊车规划算法,改进的混合A星泊入泊出规划 含parkin parkout 支持垂直,水平车...
  • ChatGLM3-6B部署与Web集成:Gradio/Streamlit/FastAPI三种方案
  • 2026年优质普洱古树茶饼推荐榜稀缺原料之选:昆明古树茶/普洱白茶/普洱红茶/云南古树茶/云南普洱荼/古树白茶/选择指南 - 优质品牌商家
  • 云容笔谈·东方红颜影像生成系统:从PS软件下载到AI生成,数字艺术创作流程革新
  • 相机传感器尺寸与光圈F值的实战解析:如何选择最佳组合
  • springboot基于微信小程序的课堂在线学习系统教学辅助平台设计与实现
  • Qwen3-ASR-1.7B GPU算力优化实践:显存占用降低35%,吞吐提升2.1倍
  • 2026 天津离婚律师推荐 遗产纠纷律师官方联系电话 400-0073-869 - 外贸老黄
  • Leather Dress Collection 模型微调入门:使用Ollama管理本地模型与数据
  • 造相-Z-Image-Turbo服务监控大屏:使用Web技术实现可视化运维
  • 11倍速无头浏览器革命:Lightpanda如何重新定义自动化性能边界
  • Ostrakon-VL-8B惊艳成果:生成带AI批注的整改前/后对比图,用于员工培训
  • MusePublic艺术创作引擎新手入门:5分钟学会中英混合Prompt,生成惊艳艺术人像
  • 从实验室到办公室:华三交换机Telnet配置的‘安全加固’与‘简化登录’实战
  • Adafruit BD3491FS音频DSP驱动库详解:嵌入式实时音效处理