C/C++相对论——C++中为什么要使用异常?
C++中为什么要使用异常?
很多人也许知道C++中的异常机制,很多人也许不知道。很多人知道C中常用的assert
,也知道在编译时候指定NODEBUG
来忽略它。
对于C语言,使用正常的if-else
即是很好的选择,而在C++中,如果使用了面向对象的编程,最好还是使用Exception
机制。这主要设计对象能否正确的析构的问题。
C中的出错跳转setjmp
和longjmp
C语言中常用的用于处理异常的方法。它不像abort
或者assert
或者exit
那样直接退出,也不像goto
语句仅仅局限在函数内部。
它是用于一种长跳转的方式。可以从一个函数跳到这个函数上层的调用函数中。
举个例子
- 函数 A 中调用了
setjmp
设置了一个跳转位,然后函数A调用了函数B。 - 函数 B 中调用了
longjmp
,那么会使得程序条到 函数 A中调用setjmp
的位置继续执行。
这不是本文的重点。
使用setjmp
和longjmp
最大的缺点是可能会跳过某些对象的构造或者析构。
还有,在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++中使用setjmp
和longjmp
造成的不良后果
我们先看代码
无法正常析构对象的代码
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