raii封装文件句柄首选std::unique_ptr配函数指针删除器,确保异常安全;fstream需显式close并检查fail()以确认持久化;状态判断应统一用explicit operator bool();win32 handle封装须判invalid_handle_value。

RAII 封装文件句柄:用 std::unique_ptr 管理 FILE*
直接用 fopen/fclose 容易忘关文件,尤其在异常路径或提前返回时。C++ 的 RAII 不是只能靠自定义类——std::unique_ptr 配合自定义删除器就能快速搞定。
关键不是“写个类”,而是让资源生命周期和对象生命周期严格绑定:
-
std::unique_ptr<file void></file>是最轻量、零成本的封装方式 - 删除器必须是函数指针类型(不能是 lambda,除非转成
std::function,但会引入堆分配) - 构造时用
fopen结果初始化,空指针会被安全处理(std::unique_ptr允许空值)
auto file_deleter = [](FILE* f) { if (f) fclose(f); };
using FilePtr = std::unique_ptr<file decltype>;
FilePtr fp(fopen("data.txt", "r"), file_deleter);
if (!fp) { /* handle error */ }
// 出作用域自动 fclose,包括异常 unwind 路径
</file>
用 std::fstream 时为什么还要手动调用 close()?
std::fstream 析构函数确实会调用 close(),但问题出在「关闭失败不抛异常」且「错误被静默丢弃」——你根本不知道写入是否真正落盘。
典型踩坑场景:程序退出前缓存未刷新、磁盘满、NFS 挂载点断开:
- 析构时
close()失败,failbit被置位,但没人检查 -
std::ofstream析构不 throw,也不报告errno,错误信息彻底丢失 - 若需确认数据已持久化,必须显式调用
close()并检查!os.fail()
所以实战中更推荐:
std::ofstream os("log.txt");
os <h3>自己写 RAII 类时,<code>operator bool()</code> 和 <code>is_open()</code> 哪个该用?</h3><p>别依赖 <code>is_open()</code> 判断有效性——它只反映构造/打开状态,不反映后续 I/O 是否出错;而 <code>operator bool()</code> 是流的「goodbit」检查,包含读写失败、EOF、格式错误等综合状态。</p><p>例如:</p>
- 文件成功打开,但后续
write()因磁盘满失败 →is_open()仍返回true,operator bool()返回false - 用
std::ifstream读到末尾 →is_open()为true,但operator bool()在下一次读失败后变false - RAII 类对外暴露状态,应统一用
explicit operator bool(),语义清晰且支持if (file) { ... }
Windows 下 CreateFile + CloseHandle 的 RAII 封装要点
Win32 句柄不是整数也不是指针,类型是 HANDLE(本质是 void*),但值为 INVALID_HANDLE_VALUE 表示无效,不能直接用 nullptr 判断。
封装时注意三点:
- 自定义删除器必须先判
h != INVALID_HANDLE_VALUE,再调CloseHandle(h) -
std::unique_ptr的第二个模板参数得是函数指针类型,比如void(*)(HANDLE) - 不要用
std::shared_ptr——CloseHandle不是线程安全的,多处共享句柄再 close 会 crash
auto handle_deleter = [](HANDLE h) {
if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
};
using HandlePtr = std::unique_ptr<handle__ decltype>;
// 注意:HANDLE 是 typedef void*, 所以这里用 HANDLE__ 防止编译器警告
// 实际中建议用包装类或别名屏蔽细节
</handle__>
析构自动 close 听起来简单,但真正落地时,错误检测时机、跨平台句柄语义差异、以及“关闭失败”本身是否算错误——这些地方最容易被跳过。尤其在日志、配置写入这类看似低风险场景里,静默失败反而最难排查。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











