黔东南苗族侗族自治州网站建设_网站建设公司_云服务器_seo优化
2026/1/7 21:25:06 网站建设 项目流程

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 │ │ │ └────────────────────────────────────────────────────────┘

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询