能,但需自定义删除器调用fclose();默认删除器用delete导致资源泄漏、缓冲区丢失;删除器须判空,类型需显式指定,不可用std::default_delete或std::function。

std::unique_ptr 能不能管 FILE*?能,但得自己写删除器
不能直接用 std::unique_ptr<file></file> 或 std::unique_ptr<file></file> 默认构造——默认删除器调的是 delete,而 FILE* 必须用 fclose() 关闭,否则资源泄漏、缓冲区丢失数据。
正确做法是传入自定义删除器(lambda 或函数对象),让它在析构时调 fclose():
auto file_deleter = [](FILE* fp) {
if (fp) fclose(fp);
};
std::unique_ptr<FILE, decltype(file_deleter)> fp(fopen("log.txt", "w"), file_deleter);注意:删除器类型必须作为模板参数显式写出,否则编译失败;fopen() 返回可能为 nullptr,所以删除器里要判空。
为什么不能用 std::default_delete?
std::default_delete 对裸指针只做 delete ptr,它不认 FILE* 是 C 运行时分配的句柄,更不会调 fclose()。强行用会导致:
立即学习“C++免费学习笔记(深入)”;
- 程序退出前未刷新缓冲区,文件内容残缺
- 句柄未释放,多开几次就
EMFILE报错 - Windows 下可能触发 CRT 断言或崩溃
fopen 失败时 unique_ptr 会不会出问题?
不会。只要构造时传入的是 nullptr,unique_ptr 会正常持有空状态,删除器也会被调用(所以删除器里必须判空)。
常见误操作:
- 先
fopen()再检查返回值,再构造unique_ptr—— 中间若抛异常,FILE*就泄露了 - 把
fopen()放进构造函数参数列表但没包 try/catch —— 构造失败时FILE*已分配却无人接管
安全写法是「立即构造」+「判空检查」:
auto fp = std::unique_ptr<FILE, void(*)(FILE*)>(fopen("data.bin", "rb"),
[](FILE* f) { if (f) fclose(f); });
if (!fp) {
// 处理打开失败
}要不要封装成类型别名?看项目规模
小项目直接用带删除器的 unique_ptr 更清晰;大项目或频繁使用可封装:
struct file_closer {
void operator()(FILE* fp) const { if (fp) fclose(fp); }
};
using unique_file = std::unique_ptr<FILE, file_closer>;注意:别用 std::function<void></void> 做删除器类型——它有虚调用开销且无法内联,而 lambda 或函数对象零成本。
还有个细节容易漏:C++17 起 std::unique_ptr 的删除器类型必须是 CopyConstructible 或 MoveConstructible,lambda 若捕获变量(比如日志上下文)就可能不可移动,此时得改用函数对象。



















