You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将ofstream作为结构体成员用作函数参数?

问题解决: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改成动态分配,这样对象会在堆上,函数返回后不会被销毁,直到你手动释放:

  1. 修改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;
}
  1. 修改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;
    }
}
  1. 程序结束时释放动态分配的流,避免内存泄漏:
// 在主函数catch块之后添加
if (log.stream != nullptr) {
    delete log.stream;
    log.stream = nullptr;
}

方案2:改用智能指针(更安全,自动管理内存)

如果你不想手动管理内存,可以用std::unique_ptr自动处理内存释放:

  1. 修改LOG结构体:
#include <memory> // 包含智能指针头文件

struct LOG {
    wchar_t logfile[PARM_MAX_SIZE];
    std::unique_ptr<std::ofstream> stream;
};
  1. 修改INITLOG:
static const LOG INITLOG {L"", nullptr};
  1. 修改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;
}
  1. 修改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包装引用:

  1. 修改LOG结构体:
#include <functional> // 包含reference_wrapper
#include <optional>

struct LOG {
    wchar_t logfile[PARM_MAX_SIZE];
    std::optional<std::reference_wrapper<std::ofstream>> stream;
};
  1. 修改INITLOG:
static const LOG INITLOG {L"", std::nullopt};
  1. 调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:15:30