“setjmp和longjmp並不能很好地支持C++中面向對象的語義。因此在C++程序中,請使用C++提供的異常處理機制”。它在MSDN中的原文如下:
setjmp and longjmp do not support C++ object semantics. In C++ programs, use the C++ exception-handling mechanism.
這究竟是為什麼?大家知道,C++語言中是基本兼容C語言中的語義的。但是為什麼,在C++程序中,唯獨卻不能使用C語言中的異常處理機制?雖然大家都知道,在C++程序中,實際上是沒有必要這麼做,因為C++語言中提供了更完善的異常處理模型。但是,在許多種特殊情況下,C++語言來開發的應用程序系統中,可能采用了C語言中的異常處理機制。例如說,一個應用程序由於采用C++開發,它裡面使用了C++提供的異常處理機制;但是它可能需要調用其它已經由C語言實現的程序庫,而恰恰在這個被復用的程序庫中,它也采用了異常處理機制。因此對於整個應用程序系統而言,它不可避免地出現了這種矛盾的局面。並且這種情況是非常多見的,也可能是非常危險的。因為畢竟,“setjmp and longjmp do not support C++ object semantics”。所以,我們非常有必要來了解它究竟為什麼不會兼容。
在本篇文章中,主人公阿愚將和程序員朋友們一起,深入探討setjmp與longjmp機制,為什麼它很難與C++和睦相處?另外還有,如果C++語言來開發的應用程序系統中,不得不同時使用這兩種異常處理模型時,又如何來盡可能保證程序系統的安全?
C++語言中使用setjmp與longjmp
閒話少說,還是看例程先吧!代碼如下:
// 注意,這是一個C++程序。文件擴展名應該為.cpp或其它等。例如,c++setjmp.cpp
#include
#include
#include
//定義一個測試類
class MyTest
{
public:
MyTest ()
{
printf("構造一個MyTest類型的對象\n");
}
virtual ~ MyTest ()
{
printf("析構銷毀一個MyTest類型的對象\n");
}
};
jmp_buf mark;
void test1()
{
// 注意,這裡拋出異常
longjmp(mark, 1);
}
void test()
{
test1();
}
void main( void )
{
int jmpret;
// 設置好異常出現時,程序的回溯點
jmpret = setjmp( mark );
if( jmpret == 0 )
{
// 建立一個對像
MyTest myobj;
test();
}
else
{
printf("捕獲到一個異常\n");
}
}
請編譯運行一下,程序的運行結果如下:
構造一個MyTest類型的對象
析構銷毀一個MyTest類型的對象
捕獲到一個異常
上面的程序運行結果,那麼到底是不是合理的呢?阿愚感到有些納悶,這結果肯定是合乎情理的呀!從這個例程來看,setjmp和longjmp並不能破壞C++中面向對象的語義,它們之間融洽得很好呀!那麼為什麼會說,“setjmp and longjmp do not support C++ object semantics”。請不要著急,沉住氣!繼續看看其它的情況,代碼如下:
#include
#include
#include
class MyTest
{
public:
MyTest ()
{
printf("構造一個MyTest類型的對象\n");
}
virtual ~ MyTest ()
{
printf("析構銷毀一個MyTest類型的對象\n");
}
};
jmp_buf mark;
void test1()
{
// 注意,這裡在上面程序的基礎上,進行了一點小小的改動
// 把對像的構造挪到這裡來
MyTest myobj;
longjmp(mark, 1);
}
void test()
{
test1();
}
void main( void )
{
int jmpret;
jmpret = setjmp( mark );
if( jmpret == 0 )
{
test();
}
else
{
printf("捕獲到一個異常\n");
}
}
同樣也編譯運行一下,看程序的運行結果,如下:
構造一個MyTest類型的對象
捕獲到一個異常
呵呵!那個對像的構造建立過程只不過是被稍稍挪了一下位置,而且先後順序還沒有改變,都是在if( jmpret == 0 )語句之後,longjmp(mark, 1)之前。可為什麼程序運行的結果卻不同了呢?顯然,從這個例程的運行結果來看,setjmp和longjmp已經破壞C++中面向對象的語義,因為那個MyTest類型的對像只被構造了,但是它卻沒有被析構銷毀!這與大家所知道的面向對象的理論是相違背的。程序員朋友們,不要小看這個問題,有時這種錯誤將給應用程序系統帶來極大的災難(不僅僅是內存資源得不到釋放,更糟糕的是可能引發系統死鎖或程序崩潰)。
由此可以看出,setjmp與longjmp機制,有時的確是不能夠與C++和睦相處。那麼,為什麼第1個例子中會安然無恙呢?它有什麼規律嗎?請繼續看另外的一個例子,代碼如下:
#include
#include
#include
class MyTest
{
public:
MyTest ()
{
printf("構造一個MyTest類型的對象\n");
}
virtual ~ MyTest ()
{
printf("析構銷毀一個MyTest類型的對象\n");
}
};
jmp_buf mark;
void test1()
{
longjmp(mark, 1);
}