C/C++相对论——C++中为什么要使用异常?

C++中为什么要使用异常?

很多人也许知道C++中的异常机制,很多人也许不知道。很多人知道C中常用的assert,也知道在编译时候指定NODEBUG来忽略它。

对于C语言,使用正常的if-else即是很好的选择,而在C++中,如果使用了面向对象的编程,最好还是使用Exception机制。这主要设计对象能否正确的析构的问题。

C中的出错跳转setjmplongjmp

C语言中常用的用于处理异常的方法。它不像abort或者assert或者exit那样直接退出,也不像goto语句仅仅局限在函数内部。
它是用于一种长跳转的方式。可以从一个函数跳到这个函数上层的调用函数中。
举个例子

  1. 函数 A 中调用了setjmp设置了一个跳转位,然后函数A调用了函数B。
  2. 函数 B 中调用了longjmp,那么会使得程序条到 函数 A中调用setjmp的位置继续执行。

这不是本文的重点。

使用setjmplongjmp最大的缺点是可能会跳过某些对象的构造或者析构。
还有,在C中使用goto可以跳过某些变量的定义,但是这不会出什么问题。可以试试下面的代码。注意,是C语言,你要是用C++的编译器来编译,应该是会报错的。

 1 #include <stdio.h>

 2 

 3 int main(int argc,char** argv)

 4 {

 5     if(argc > 1){

 6         goto nodef;

 7     }

 8     int a = 102;

 9 nodef:

10     printf(" a = %d\n",a);

11     return 0;

12 }

 

C++中使用setjmplongjmp造成的不良后果

我们先看代码

无法正常析构对象的代码

 1 #include <iostream>

 2 #include <csetjmp>

 3 

 4 using std::cout;

 5 using std::endl;

 6 

 7 class Test{

 8     public:

 9         Test(){ cout<<"Test 构造"<<endl;}

10         ~Test(){cout<<"Test 析构"<<endl;}

11 };

12 

13 jmp_buf jbuf;   //用于setjmp保存当前相关信息

14 

15 void calljmp()

16 {

17     Test t; //测试能够正确调用析构

18     cout<<"call longjmp(jbuf,3721)"<<endl;

19     longjmp(jbuf,3721);

20 }

21 

22 int main()

23 {

24     int ret=0;

25     if( 0 == (ret=setjmp(jbuf))){

26         cout<<"call setjmp(jbuf) resuces"<<endl;

27         calljmp();

28     }

29     else{

30         cout<<"call setjmp(jbuf) failed   ret = "<< ret <<endl;

31     }

32 }

 

编译执行看看

可以看到,对象构造了,但是没有正常的调用析构。

1 o@o-pc:~/code_/exception$ g++ setjmp.cpp -o setjmp

2 o@o-pc:~/code_/exception$ ./setjmp 

3 call setjmp(jbuf) resuces

4 Test 构造

5 call longjmp(jbuf,3721)

6 call setjmp(jbuf) failed   ret = 3721

 

C++中使用异常处理的情况

C++中使用异常机制的好处之一,就是能够正确的去析构对象。

使用了异常处理机制的代码

 1 #include <iostream>

 2 #include <csetjmp>

 3 

 4 using std::cout;

 5 using std::endl;

 6 

 7 class Test{

 8     public:

 9         Test(){ cout<<"Test 构造"<<endl;}

10         ~Test(){cout<<"Test 析构"<<endl;}

11 };

12 

13 jmp_buf jbuf;   //用于setjmp保存当前相关信息

14 

15 void calljmp()

16 {

17     Test t; //测试能够正确调用析构

18     cout<<"call longjmp(jbuf,3721)"<<endl;

19     //longjmp(jbuf,3721);

20     throw 3721;

21 }

22 

23 int main()

24 {

25     try{

26         cout<<"调用calljmp 尝试抛出异常"<<endl;

27         calljmp();

28     }catch(int t){

29         cout<<"捕获到异常值:"<<t<<endl;

30     }

31     /*

32     int ret=0;

33     if( 0 == (ret=setjmp(jbuf))){

34         cout<<"call setjmp(jbuf) resuces"<<endl;

35         calljmp();

36     }

37     else{

38         cout<<"call setjmp(jbuf) failed   ret = "<< ret <<endl;

39     }

40     */

41 }

 

编译运行试试

可以看到这次正常调用了析构函数

o@o-pc:~/code_/exception$ g++ exception.cpp -o exception

o@o-pc:~/code_/exception$ ./exception

调用calljmp 尝试抛出异常

Test 构造

call longjmp(jbuf,3721)

Test 析构

捕获到异常值:3721

 

你可能感兴趣的:(c/c++)