如何将ofstream作为结构体成员用作函数参数?
问题描述
我尝试在WriteLine()函数中使用getlog()函数里打开的ofstream,定义了如下LOG结构体:
struct LOG { wchar_t logfile[PARM_MAX_SIZE]; std::ofstream* stream; };
该结构体作为WriteLine()的参数,但使用流时出现读取错误。我试过用LOG变量的引用,也尝试把ofstream*转成ofstream&,但不知道怎么正确操作——因为LOG的初始化代码是:
static const LOG INITLOG {L"", NULL};
不知道怎么修改才能用流的引用。
相关函数代码:
LOG getlog(wchar_t logfile[]) { ofstream logf(logfile, ios::app); if (!logf.is_open()) { throw ERROR_THROW(112) } LOG log{}; wcscpy_s(log.logfile, logfile); log.stream = &logf; return log; } void WriteLine(LOG log, char* c, ...) { char str[80]{}; char** p = &c; while (*p[0]) { strcat_s(str, *p); p++; } str[strlen(str)] = '\0'; ofstream* logf = log.stream; *logf << "str"; }
主函数代码:
int _tmain(int argc, wchar_t* argv[]) { setlocale(LC_ALL, "rus"); Log::LOG log = Log::INITLOG; try { Parm::PARM parms = Parm::getparm(argc, argv); log = Log::getlog(parms.log); Log::WriteLine(log, (char*)"Тест:", (char*)" без ошибок \n", ""); /*Log::WriteLine(log, (wchar_t*)L"Тест:", (wchar_t*)L" без ошибок \n", L""); Log::WriteLog(log);*/ } catch (Error::ERROR e) { cout << "Ошибка " << e.id << ": " << e.message << ", строка " << e.inext.line << ", позиция " << e.inext.col << endl << endl; } system("pause"); return 0; }
核心问题分析
getlog()函数里的logf是局部栈对象,函数返回时这个对象会被销毁,对应的内存被释放。你把&logf赋值给log.stream,返回的结构体里保存的是一个指向已销毁对象的悬空指针,在WriteLine()里访问这个指针就会触发未定义行为(也就是你遇到的读取错误)。
另外,如果你想改用ofstream&,因为引用必须绑定到一个有效的对象,且不能被重新赋值,直接替换指针会和INITLOG的初始化冲突(NULL不能初始化引用),所以需要调整结构体的设计和内存管理方式。
解决方案
方案1:动态分配ofstream(最直接适配现有代码)
把局部的ofstream改成动态分配,这样对象会在堆上,函数返回后不会被销毁,直到你手动释放:
- 修改
getlog()函数:
LOG getlog(wchar_t logfile[]) { // 动态分配ofstream,用new创建堆对象 std::ofstream* logf = new std::ofstream(logfile, std::ios::app); if (!logf->is_open()) { delete logf; // 分配失败要释放,避免内存泄漏 throw ERROR_THROW(112); } LOG log{}; wcscpy_s(log.logfile, logfile); log.stream = logf; return log; }
- 修改
WriteLine(),传LOG的引用避免拷贝,同时增加指针有效性判断:
// 传LOG的引用,减少拷贝,也避免指针意外问题 void WriteLine(LOG& log, char* c, ...) { char str[80]{}; char** p = &c; while (*p[0]) { strcat_s(str, *p); p++; } if (log.stream != nullptr) { // 先判断指针是否有效 *log.stream << str; } }
- 程序结束时释放动态分配的流,避免内存泄漏:
// 在主函数catch块之后添加 if (log.stream != nullptr) { delete log.stream; log.stream = nullptr; }
方案2:改用智能指针(更安全,自动管理内存)
如果你不想手动管理内存,可以用std::unique_ptr自动处理内存释放:
- 修改LOG结构体:
#include <memory> // 包含智能指针头文件 struct LOG { wchar_t logfile[PARM_MAX_SIZE]; std::unique_ptr<std::ofstream> stream; };
- 修改INITLOG:
static const LOG INITLOG {L"", nullptr};
- 修改
getlog()函数:
LOG getlog(wchar_t logfile[]) { auto logf = std::make_unique<std::ofstream>(logfile, std::ios::app); if (!logf->is_open()) { throw ERROR_THROW(112); } LOG log{}; wcscpy_s(log.logfile, logfile); log.stream = std::move(logf); // 转移智能指针的所有权 return log; }
- 修改
WriteLine():
void WriteLine(LOG& log, char* c, ...) { char str[80]{}; char** p = &c; while (*p[0]) { strcat_s(str, *p); p++; } if (log.stream) { // 智能指针可以直接判断是否有效 *log.stream << str; } }
这种方式不需要手动delete,智能指针会在LOG对象销毁时自动释放ofstream。
方案3:改用引用(需调整初始化逻辑)
如果一定要用ofstream&,需要保证引用始终绑定到有效的对象,可以用std::optional包装引用:
- 修改LOG结构体:
#include <functional> // 包含reference_wrapper #include <optional> struct LOG { wchar_t logfile[PARM_MAX_SIZE]; std::optional<std::reference_wrapper<std::ofstream>> stream; };
- 修改INITLOG:
static const LOG INITLOG {L"", std::nullopt};
- 调整
getlog(),让ofstream的生命周期覆盖LOG的使用期(比如用静态对象):
LOG getlog(wchar_t logfile[]) { static std::ofstream logf(logfile, std::ios::app); // 静态对象,生命周期和程序一致 if (!logf.is_open()) { throw ERROR_THROW(112); } LOG log{}; wcscpy_s(log.logfile, logfile); log.stream = logf; // 绑定到静态对象的引用 return log; }
注意:静态ofstream会被多次调用getlog()复用,若需要每次调用都创建新流,这种方式不适用。
额外优化建议
WriteLine()的参数改用const char*更安全,避免意外修改:void WriteLine(LOG& log, const char* c, ...)- 处理可变参数时,建议用标准的
va_list代替手动遍历指针,代码更健壮:
#include <cstdarg> void WriteLine(LOG& log, const char* format, ...) { char str[80]{}; va_list args; va_start(args, format); vsnprintf_s(str, sizeof(str), _TRUNCATE, format, args); va_end(args); if (log.stream) { *log.stream << str; } }
调用时更直观:Log::WriteLine(log, "Тест: %s \n", "без ошибок");
内容的提问来源于stack exchange,提问作者kostanchika

