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 supportparagraph 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/

10-11 17:59