Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2275688
  • 博文数量: 668
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 8588
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-29 19:22
文章分类

全部博文(668)

文章存档

2011年(1)

2010年(2)

2009年(273)

2008年(392)

分类:

2008-08-07 08:57:23

在《第16集 中一种更优雅的异常处理机制》中,就已经提到过,“setjmp和longjmp并不能很好地支持中面向对象的语义。因此在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!

阅读(595) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~