最简可靠方式是直接修改 std::cout 的 rdbuf(),用 std::ofstream 的 buffer 替换并确保其生命周期足够长;需注意缓冲策略,推荐用 std::endl 或手动 flush 保证日志实时落盘。

std::cout 重定向到文件的最简可靠方式
直接修改 std::cout 的 rdbuf() 是最轻量、最可控的做法,不依赖系统调用或进程级重定向,适合单线程日志场景。
常见错误是用 freopen() 重定向 stdout 后,std::cout 仍可能因缓冲策略不同而输出错乱——因为 C++ 流和 C stdio 流的缓冲层不完全同步。
- 先用
std::ofstream打开日志文件,确保.is_open()为真 - 保存原始 buffer:
auto old_buf = std::cout.rdbuf(file_stream.rdbuf()); - 后续所有
std::cout 都会写入该文件,直到你调用std::cout.rdbuf(old_buf)恢复 - 注意:
file_stream生命周期必须长于重定向期,否则 buffer 失效导致未定义行为
缓冲策略决定日志是否实时落盘
默认情况下 std::cout 在重定向后仍继承行缓冲(遇到 '\n' 才刷),但文件流本身是全缓冲——这意味着程序崩溃或提前退出时,最后一段日志可能丢失。
- 强制立即写入:在每次输出后加
或(后者还额外换行) - 关闭缓冲(慎用):
file_stream.rdbuf()->pubsetbuf(nullptr, 0);,但会显著降低性能 - 更实用的做法:构造
std::ofstream时启用自动刷新:std::ofstream log_file("app.log", std::ios::out | std::ios::app); log_file —— 这会让每次插入操作后自动 flush
多线程下 std::cout 重定向的风险
重定向是全局的,一旦修改 std::cout.rdbuf(),所有线程共用同一个目标 buffer。没有内置锁,多个线程同时写会导致日志内容交错、断行错乱。
- 不要在多线程环境里直接重定向
std::cout做日志 - 替代方案:封装一个线程安全的 logger 类,内部用
std::mutex+std::ofstream,或使用spdlog等成熟库 - 若坚持用
std::cout,至少确保重定向只发生在主线程初始化阶段,且之后所有日志都通过统一接口写入,避免裸用
Windows 下中文路径/内容乱码问题
在 Windows 控制台环境下,std::ofstream 默认按本地 ANSI 编码(如 GBK)打开文件,但源码字符串常量通常是 UTF-8(尤其 VS2019+ 默认源文件编码),直接写入会导致日志文件中文乱码。
- 显式指定宽字符流:
std::wofstream log_file(L"日志.txt"); log_file.imbue(std::locale("")); // 使用系统 locale - 或统一转 UTF-8 并用
std::ofstream配合 BOM:手动写入\xEF\xBB\xBF,再以std::ios::binary模式写 UTF-8 字节 - VS 用户可设置项目属性 → 高级 → “字符集” → “使用 Unicode 字符集”,再配合
std::wcout和std::wofstream
实际落地时,最易被忽略的是 buffer 生命周期和线程安全性——前者导致程序运行中突然日志停止,后者让问题只在高并发时偶然暴露,调试成本远高于初期加个 mutex。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











