快捷搜索:

第19集 setjmp与longjmp机制,很难与C++和睦相处

在《第16集 C说话中一种更优雅的非常处置惩罚机制》中,就已经提到过,“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);

}

void test()

{

/ // 留意,现在又把它挪到了这里

MyTest myobj;

test1();

}

void main( void )

{

int jmpret;

jmpret = setjmp( mark );

if( jmpret == 0 )

{

test();

}

else

{

printf("捕获到一个非常\n");

}

}

请编译运行一下,法度榜样的运行结果如下:

构造一个MyTest类型的工具

析构销毁一个MyTest类型的工具

捕获到一个非常

呵呵!这里的运行结果也是对的。以是主人公阿愚总结出了一条结论,那便是,“在longjmp被调用履行的那个函数感化域中,绝对不能够存在局部变量形式的工具(也即在客栈中的工具,longjmp履行时还没有被析构销毁),否则这些工具将得不到析构的时机”。切忌切忌!又例如下面的例子同样也会有问题,代码如下:

#include

#include

#include

class MyTest

{

public:

MyTest ()

{

printf("构造一个MyTest类型的工具\n");

}

virtual ~ MyTest ()

{

printf("析构销毁一个MyTest类型的工具\n");

}

};

jmp_buf mark;

void main( void )

{

int jmpret;

jmpret = setjmp( mark );

if( jmpret == 0 )

{

MyTest myobj;

longjmp(mark, 1);

}

else

{

printf("捕获到一个非常\n");

}

}

总结

虽然说,setjmp与longjmp机制,很难与C++和蔼相处。然则它并非那么可骇,只要掌握了它的规律,全部利用法度榜样系统的安然性仍掌握在你手心。而且,本文开首提到的例子(采纳C++开拓的利用法度榜样,它里面调用了C说话实现的其它法度榜样库,并且库代码中应用了setjmp和longjmp机制),它并没有任何的问题。由于这个C库中,此中调用longjmp的函数域内,决不会有工具的构造定义。

现在为止,对setjmp和longjmp的评论争论暂告一个段落。在“爱的秘密”篇中,会进一步阐述它的实现。下一篇文章将评论争论在C++中,若何兼容并支持C说话中供给的其它要领的非常处置惩罚机制(例如,C++中对goto语句的支持)。哈哈! goto next!

您可能还会对下面的文章感兴趣: