在 longjmp/croak 之前显式调用析构函数

3
我正在编写一个将PERL XS接口与C++库相结合的程序。当库抛出异常时,我需要调用croak
直接在异常处理程序中执行此操作会导致无法调用已捕获异常的析构函数,这是longjmp调用所期望的。这很重要,因为异常包含字符串成员,如果不释放,就会造成内存泄漏。
显而易见的解决方案是在catch块之后执行croak,如果已捕获异常,则像这样:
bool do_croak = false;
try {
    throw MyException();
} catch (MyException &e) {
    do_croak = true;
}
if (do_croak)
    croak(NULL);

但我在想:只在longjmp之前显式调用捕获的异常的析构函数是否已足够?像这样:

try {
    throw MyException();
} catch (MyException &e) {
    e.~MyException();
    croak(NULL);
}
1个回答

4
在C++程序中使用longjmp是几乎不可能安全的。具体来说,C++11标准规定当用catchthrow替换setjmplongjmp时,如果为任何自动对象调用非平凡析构函数,则setjmp/longjmp调用对应的行为就是未定义的,这意味着从croak抛出异常会调用e的析构函数,因此从那里调用longjmp会产生未定义的行为。直接调用析构函数只会使行为更加不确定。

嗯,但是我提到的“显而易见的方法”(请参见更新问题中的新代码)应该是合理的,假设除了catch之外只有POD?(我正在使用C++98/03) - Irfy
@Irfy:我不认为未定义行为是“合理的”。如果你的意思是大多数编译器最终会正确销毁e,尽管这种行为是未定义的,那么你可能是对的;但在我看来,依赖未定义的行为是一个非常糟糕的想法。 - Mike Seymour
也许你误解了我所说的方法。我指的是在try-catch之后发生croak的方法。在那里用catch替换croak不会导致任何析构函数运行,因为e已经被销毁了。或者你也认为那里存在未定义的行为? - Irfy
@Irfy:抱歉,我没有注意到问题的更改。只要您满足不跳出具有非平凡析构函数的任何范围的要求,那么这确实是明确定义的。当然,这仍然非常危险,因为没有办法强制执行该要求。 - Mike Seymour
好的,那我就坚持我的原始方式。 - Irfy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接