news 2026/5/19 9:23:27

【C/C++】RAII,Stack-only对象和Heap-only对象

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C/C++】RAII,Stack-only对象和Heap-only对象

RAII 详解

什么是 RAII

Resource Acquisition Is Initialization(资源获取即初始化)

核心思想:把资源的生命周期绑定到对象的生命周期

  • 构造函数获取资源
  • 析构函数释放资源
  • 对象销毁时,资源自动释放

FileHandler 示例

没有 RAII 的写法

voidprocess(){FILE*f=fopen("data.txt","r");if(some_error){return;// 忘记 fclose,文件泄漏!}do_something();// 如果抛异常,也泄漏!fclose(f);}

问题:每个退出路径都要手动关闭文件,容易遗漏。

RAII 版本

// file_handler.hpp#pragmaonce#include<cstdio>#include<stdexcept>#include<string>classFileHandler{FILE*fp;public:// 构造时打开文件(获取资源)FileHandler(constchar*path,constchar*mode):fp(fopen(path,mode)){if(!fp){throwstd::runtime_error("Cannot open file: "+std::string(path));}}// 析构时关闭文件(释放资源)~FileHandler(){if(fp){fclose(fp);}}// 禁止拷贝(避免双重关闭)FileHandler(constFileHandler&)=delete;FileHandler&operator=(constFileHandler&)=delete;// 允许移动FileHandler(FileHandler&&other)noexcept:fp(other.fp){other.fp=nullptr;}FileHandler&operator=(FileHandler&&other)noexcept{if(this!=&other){if(fp)fclose(fp);fp=other.fp;other.fp=nullptr;}return*this;}// 使用文件FILE*get()const{returnfp;}size_tread(void*buf,size_t size){returnfread(buf,1,size,fp);}size_twrite(constvoid*buf,size_t size){returnfwrite(buf,1,size,fp);}};

使用方式

#include"file_handler.hpp"#include<iostream>voidprocess(){FileHandlerfile("data.txt","r");// 打开文件if(some_error){return;// 自动关闭!}do_something();// 异常也自动关闭!charbuf[1024];file.read(buf,sizeof(buf));}// 离开作用域,自动关闭intmain(){try{FileHandlerf1("input.txt","r");FileHandlerf2("output.txt","w");charbuf[4096];size_t n;while((n=f1.read(buf,sizeof(buf)))>0){f2.write(buf,n);}}catch(conststd::exception&e){std::cerr<<e.what()<<"\n";// f1, f2 已经自动关闭了}}

Stack Only 版本

强制 FileHandler 只能在栈上创建,确保 RAII 生效:

// file_handler_stack.hpp#pragmaonce#include<cstdio>#include<stdexcept>classFileHandler{FILE*fp;public:FileHandler(constchar*path,constchar*mode):fp(fopen(path,mode)){if(!fp)throwstd::runtime_error("Cannot open file");}~FileHandler(){if(fp)fclose(fp);}// 禁止拷贝FileHandler(constFileHandler&)=delete;FileHandler&operator=(constFileHandler&)=delete;// 禁止堆分配,强制栈上使用void*operatornew(size_t)=delete;void*operatornew[](size_t)=delete;FILE*get()const{returnfp;}};

使用:

voidprocess(){FileHandlerfile("data.txt","r");// ✓ 栈上,自动管理// FileHandler* p = new FileHandler(); // ✗ 编译错误}

为什么要 Stack Only?防止这种情况:

voidbad(){FileHandler*p=newFileHandler("data.txt","r");// 忘记 delete p,文件永远不会关闭}

Heap Only 版本

有时需要共享文件句柄,使用引用计数:

// file_handler_heap.hpp#pragmaonce#include<cstdio>#include<memory>#include<stdexcept>classFileHandler{FILE*fp;// 私有构造/析构,只能通过 create() 创建FileHandler(constchar*path,constchar*mode):fp(fopen(path,mode)){if(!fp)throwstd::runtime_error("Cannot open file");}~FileHandler(){if(fp)fclose(fp);}public:// 工厂方法,返回 shared_ptrstaticstd::shared_ptr<FileHandler>create(constchar*path,constchar*mode){autop=newFileHandler(path,mode);// lambda 在成员函数内,可以访问私有析构returnstd::shared_ptr<FileHandler>(p,[](FileHandler*f){deletef;});}FILE*get()const{returnfp;}};

使用:

voidprocess(){// FileHandler f("data.txt", "r"); // ✗ 编译错误autofile=FileHandler::create("data.txt","r");// ✓autofile2=file;// 共享所有权,引用计数 = 2// 传给其他函数/线程async_read(file);}// 引用计数归零时自动关闭

三种方式对比

┌─────────────────┬──────────────────┬──────────────────┬──────────────────┐ │ │ 普通 RAII │ Stack Only │ Heap Only │ ├─────────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 栈上创建 │ ✓ │ ✓ │ ✗ │ ├─────────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 堆上创建 │ ✓ │ ✗ │ ✓ │ ├─────────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 生命周期 │ 作用域或手动 │ 作用域 │ 引用计数 │ ├─────────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 共享所有权 │ 需要 shared_ptr │ ✗ │ ✓ (内置) │ ├─────────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 适用场景 │ 通用 │ 局部资源管理 │ 跨作用域/线程共享 │ └─────────────────┴──────────────────┴──────────────────┴──────────────────┘

总结

┌────────────────────────────────────────────────────────┐ │ RAII 核心 │ ├────────────────────────────────────────────────────────┤ │ │ │ 构造 = 获取资源 │ │ 析构 = 释放资源 │ │ 对象死亡 = 资源释放 │ │ │ ├────────────────────────────────────────────────────────┤ │ 好处 │ ├────────────────────────────────────────────────────────┤ │ │ │ • 不会忘记释放 │ │ • 异常安全 │ │ • 代码简洁 │ │ │ ├────────────────────────────────────────────────────────┤ │ 选择指南 │ ├────────────────────────────────────────────────────────┤ │ │ │ 局部使用 → Stack Only │ │ 需要共享 → Heap Only + shared_ptr │ │ 不确定 → 普通 RAII │ │ │ └────────────────────────────────────────────────────────┘
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 10:35:14

aspnet_counters.dll文件丢失找不到 打不开问题 免费下载方法分享

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华
网站建设 2026/5/5 12:14:41

springboot智能学习平台系统(11655)

有需要的同学&#xff0c;源代码和配套文档领取&#xff0c;加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码&#xff08;前后端源代码SQL脚本&#xff09;配套文档&#xff08;LWPPT开题报告&#xff09;远程调试控屏包运行 三、技术介绍 Java…

作者头像 李华
网站建设 2026/5/8 17:15:00

【开题答辩全过程】以 基于HTML5的移动端网页设计为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/5/16 3:49:48

最近在车库折腾无感FOC方案时,发现STM32F030这颗白菜价MCU居然能跑滑模观测器。今天就把这套验证通过的方案拆开看看,老铁们可以直接拿去魔改

stm32f030无感foc方案&#xff0c;资料包括原理图&#xff0c;pcb&#xff0c;源程序&#xff0c;观测器参数&#xff0c;电流环参数计算表格。先看硬件部分&#xff08;原理图在附件FOC_V1.2.pdf&#xff09;。重点在电流采样电路设计&#xff0c;这里用了双电阻采样配运放INA…

作者头像 李华
网站建设 2026/5/18 22:21:06

别慌!2026年度学生论文降重工具实测:知网AIGC降AI率神器,避免通宵崩溃,三款十佳降AI产品全解析

为什么要用学生论文降重工具&#xff1f; 每当临近论文提交&#xff0c;特别是使用知网等权威检测系统时&#xff0c;我和许多同学都会因论文AI率过高而焦虑不安&#xff0c;生怕挂科。面对严格的AIGC检测&#xff0c;单靠人工修改往往费力又费时。一篇初稿经知网AI率检测达到…

作者头像 李华