2013-04-29 83 views
3

jpeglib中,必須使用setjmp/longjmp來實現自定義錯誤處理。在setjmp被破壞之前是否創建了對象?

有很多資源,在那裏說的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() 

回答

7

可以根據析構函數會被稱爲與否都執行相同的控制轉移例外是未定義的行爲。在C++ 03中。從部分18.7 Other runtime supportparagraph 4

函數簽名longjmp(jmp_buf jbuf, int val)在本國際標準更嚴格的行爲。如果任何自動對象被拋出的異常將控制轉移到程序中的另一個(目標)點,那麼在將控制轉移到同一(目標)點的擲點處調用longjmp(jbuf, val)具有未定義的行爲。

有在C語言相似++ 11:

函數簽名longjmp(jmp_buf jbuf, int val)在本國際標準更嚴格的行爲。 A setjmp/longjmp呼叫對具有未定義的行爲,如果將setjmplongjmp替換爲catchthrow將調用任何自動對象的非平凡析構函數。

然而,似乎沒有析構函數被調用此特定的代碼轉換,所以我相信它是安全的。現在


,如果你要更改代碼後的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.

我覺得我用它在我的整個職業生涯中一次(這是一個事業),實施下的Turbo C合作社線程MS-DOS。我再也想不起我曾經用過它了。不是說有不是的任何用途,但他們會很罕見。

+0

但是在這種情況下,沒有自動對象會被異常破壞。 – john 2013-04-29 06:38:57

+0

是的,不會拋出異常。 – 2013-04-29 06:40:28

+1

@BЈовић,無論您的特定環境是否有效,都不會作爲UB的決定因素。有時UB的工作原理與您所期望的完全相同,但仍然是UB,並且可能在其他人的環境中(或在藍月亮期間或使用不同的編譯器選項等)完全不同。對於明確的信息,標準始終是控制文件。 – paxdiablo 2013-04-29 06:50:01