抛出异常时是否需要va_end?

时间:2012-07-25 08:06:08

标签: c++ exception-handling variadic-functions printf

我有一个基于printf样式格式的日志框架:

void Logger::debug(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    this->output(DebugLevel, fmt, args);
    va_end(args);
}

如果Logger::output抛出,编译器是否会正确展开堆栈,还是需要在catch子句中添加带va_end(args)的try / catch块?这可能是RAII',还是va_end太神奇了?如果可能,请包括对标准的引用。

3 个答案:

答案 0 :(得分:6)

不,他们不能。他们不能因为它们是宏的原因是愚蠢的。宏可以从构造函数和析构函数中使用而没有任何问题。但是,va_startva_end具有必须从同一函数调用它们的特定要求。将它们移动到单独的函数是无效的。 C ++指的是C标准,C标准说“va_startva_copy宏的每次调用都应该通过相同函数中va_end宏的相应调用来匹配。” (7.15.1)如果从助手类的析构函数中调用va_end,它可能会起作用,也可能不起作用。由于它不符合标准的要求,因此行为未定义。

编辑:至于另一个问题,当抛出异常时,你是否需要va_end,可以做出合法的论证“va_end宏的调用”实际上并不需要代码到达您调用该宏的位置(因为宏调用严格来说只是一个编译时操作),但强烈建议您确实需要它。是的,如果有可能存在异常,请使用try / catch。 C99基本原理在其va_copy的描述中简要地指出va_start可以分配内存。 (我知道实际上没有实现它的实现。)在这样的实现上,va_end然后将释放该内存,因此跳过va_end会导致内存泄漏。

答案 1 :(得分:1)

va_startva_end是宏。所以,他们不能RAII。此外,例外情况不需要特别小心。

答案 2 :(得分:0)

是的,这可以使用Boost.ScopeExit进行RAII,即使va_start / va_end是宏的。