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 [email protected]:~/code_/exception$ g++ setjmp.cpp -o setjmp
2 [email protected]:~/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 }

编译运行试试

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

[email protected]:~/code_/exception$ g++ exception.cpp -o exception
[email protected]-pc:~/code_/exception$ ./exception
调用calljmp 尝试抛出异常
Test 构造
call longjmp(jbuf,3721)
Test 析构
捕获到异常值:3721
时间: 2024-08-27 07:10:45

C/C++之我见——C++中为什么要使用异常?的相关文章

【06-17】开发中预到的异常

hibernate: bug: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 solution: hibernate在执行save(Object obj)时,如果obj的主键不为空,则会去update而不是save操作,此时如果obj的主键不为空,但是在数据库中查不到时则会抛出该异常,因此save时可以显示将主键字段设为Null. [06-17]开发中预到的

OpenCV访问Mat对象中数据时发生异常---Mat中的数据访问

7.1和7.1.1由于越狱不成熟,半完美越狱后电脑上无法访问系统越狱目录,如var usr 等等. 今天有些意外地发现,可以在电脑上使用手机的越狱目录我手机 i4 7.1.1 联通 半完美越狱,没装Afc2Add,也没装Appsync 附上  --->我的半完美越狱过程 好了,下面直接正题 一.前提,必须安装ifile! 打开ifile,并转到 /var/mobile/media 目录下,然后点击右上角的 [ 编辑 ]如图: 二.点左下角的 + 号创建,如图: 三.点 [ 类型],选择[符号链接

JAVA 7新特性——在单个catch代码块中捕获多个异常,以及用升级版的类型检查重新抛出异常

在Java 7中,catch代码块得到了升级,用以在单个catch块中处理多个异常.如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度.下面用一个例子来理解. Java 7之前的版本: 1 2 3 4 5 6 7 8 9 10 catch (IOException ex) {      logger.error(ex);      throw new MyException(ex.getMessage()); catch (SQLException ex) {      

Delphi中的异常处理(异常来源、处理、精确处理)

一.异常的来源 在Delphi应用程序中,下列的情况都比较有可能产生异常. 1.文件处理 2.内存分配 3.windows资源 4.运行时创建对象和窗体 5.硬件和操作系统冲突 6.网络问题 7.数据库 8.控件中的异常 9.DLL文件的异常 10.强制类型转换 ………… 二.异常的处理 1.try...except...end; 在try 体内的代码发生异常时,系统将转向except 部分进行异常的处理.这是Delphi处理异常的最基本的方式之一. 只有当try 体内的代码发生异常时,才会跳转

try ,finally都抛出异常如何处理.如果try中抛出了异常,在控制权转移到调用栈上一层代码之前, finally 语句块也会执行,如果finally抛出异常,try语句快抛出的那个异常就

package com.github.jdk7; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * try ,finally都抛出异常如何处理.如果try中抛出了异常,在控制权转移到调用栈上一层代码之前, * finally 语句块也会执行,如果finally抛出异常,try语句快抛出的那个异常就丢失了. * * @author doctor * * @since 2014年

1-hadoop中遇到的各种异常

本贴记录学习hadoop中遇到的各种异常, 包括推荐系统分类下的和本分类下的, 持续更新 1, 搭建ha时, active和standy之间不能自由切换 经检查, 配置文件错误, 私钥配置 root 前没加 / 2, eclipse插件安装好以后, 不能上传文件 插件版本 hadoop-eclipse-plugin-2.6.0 查看error.log为: "Map/Reduce location status updater".java.lang.NullPointerExceptio

Erlang中一些错误或者异常的标识

erlang中错误大体分为四种: 1. 编译错误    2. 逻辑错误    3. 运行时错误    4. 用户代码生成的错误 编译错误,主要是编译器检测出的代码语法错误 逻辑错误,是指程序没有完成预期的工作,属于开发人员的问题 运行时错误,是指erlang运行时抛出的错误,比如对非数据类型执行算术运算,erlang运行时会捕获异常,并抛出.在erlang中,这类异常的类型为error 用户自定义错误,是指通过exit/1或者throw/1生成 我们把运行时错误以及用户抛出的错误称为异常(exc

Android 捕捉app系统中未处理的异常

一:为什么要处理? 其实我们都知道,在开发过程中,自己的app系统或许有许多隐藏的异常,自己没有捕捉到,那么关于异常的捕捉,这是相当重要的,如果系统发生崩溃,那么至少也可以让系统挂在系统之内,不会发现什么系统直接退了,或者是卡死,这样做,能够使得用户体验效果更加,自己也可以发现用户到底出现什么异常,便于自己以后好处理这个问题,优化处理自己的系统. 二:如何解决 在Android 开发中,自身其实带有一个系统默认的异常处理接口,UncaughtExceptionHandler,该接口呢,能够很好的

tomcat的webappclassloader中一个奇怪的异常信息

如果一个应用抛出大量的Class not found信息,一般你会怀疑包冲突.可是tomcat的webappclassloader却有这样的问题: 如果一个应用发布出现问题, webappclassloader的started属性被设为false.然后其它线程如果继续使用webappclassloader进行class load,则大量的Class not found异常 被抛出: 1391       public Class loadClass(String name, boolean re