在 jpeglib 中,必须使用 setjmp/longjmp 来实现自定义错误处理。
有很多资源都说 setjmp/longjmp 不能很好地与 c++ 配合使用(例如 this question 中的答案告诉他们确实与 RAII 兼容),但是 this question 的答案说析构函数被调用。
我有这个例子(取自 here 并稍作修改):
#include <iostream>
#include <csetjmp>
std::jmp_buf jump_buffer;
struct A
{
A(){std::cout<<"A()"<<std::endl;}
~A(){std::cout<<"~A()"<<std::endl;}
};
void a(int count)
{
std::cout << "a(" << count << ") called\n";
std::longjmp(jump_buffer, count+1); // setjump() will return count+1
}
int main()
{
// is this object safely destroyed?
A obj;
int count = setjmp(jump_buffer);
if (count != 9) {
a(count);
}
}
在这个例子中,析构函数被调用(如我所料),但它是标准行为吗?或者它是编译器的扩展,还是简单的 UB?
输出:
A()
a(0) called
a(1) called
a(2) called
a(3) called
a(4) called
a(5) called
a(6) called
a(7) called
a(8) called
~A()
最佳答案
它可以是未定义的行为,具体取决于是否会调用析构函数作为执行相同控制转移的异常。在 C++03 中。从部分 18.7 Other runtime support
, paragraph 4
:
在 c++11 中有类似的语言:
但是,对于这段特定的代码,似乎没有在转换过程中调用析构函数,所以我相信它是安全的。
现在,如果您要更改代码以将创建移动到 setjmp
之后,那将成为未定义的行为。在我的设置(Debian 下的 gcc 4.4.5)中,以下代码(其他所有内容与您的问题相同):
int main() {
int count = setjmp (jump_buffer);
A obj;
if (count != 4) a (count);
}
结果输出:
A()
a(0) called
A()
a(1) called
A()
a(2) called
A()
a(3) called
A()
~A()
并且您可以看到析构函数没有作为跳转的一部分被调用,尽管未定义,它可能在某些系统上。
最重要的是,你不应该从区域 A 跳转到区域 B,在那里等效的
throw/catch
会正确地破坏一个对象,因为不能保证 longjmp
会调用析构函数。实际上,有些人会说你根本不应该在 C++ 中使用
setjmp/longjmp
,我自己也倾向于这样。即使在 C 中,我也很难看到需要这样做。我想我在我的整个职业生涯中都使用过一次(这是一个漫长的职业生涯),在 MS-DOS 下在 Turbo C 中实现协作线程。我想不出我曾经使用过的其他时间。并不是说没有任何用途,但它们非常罕见。
关于c++ - 在 setjmp 销毁之前创建的对象是否被破坏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16272530/