在任何一门语言中都有异常的解释,这里就不做介绍了。

C++ 异常处理机制会涉及 try、catch、throw 三个关键字。

程序错误

程序的错误大致可以分为三种,分别是语法错误、逻辑错误和运行时错误
1) 语法错误在编译和链接阶段就能发现,只有 100% 符合语法规则的代码才能生成可执行程序。语法错误是最容易发现、最容易定位、最容易排除的错误,程序员最不需要担心的就是这种错误。
2) 逻辑错误是说我们编写的代码思路有问题,不能够达到最终的目标,这种错误可以通过调试来解决。
3) 运行时错误是指程序在运行期间发生的错误,例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。

运行时错误如果放任不管,系统就会执行默认的操作,终止程序运行,也就是我们常说的程序崩溃(Crash)。C++ 提供了异常(Exception)机制,让我们能够捕获运行时错误,给程序一次“起死回生”的机会,或者至少告诉用户发生了什么再终止程序。

异常处理基本思想

C++之父Bjarne Stroustrup在《The C++ Programming Language》中讲到:一个库的作者可以检测出发生了运行时错误,但一般不知道怎样去处理它们(因为和用户具体的应用有关);另一方面,库的用户知道怎样处理这些错误,但却无法检查它们何时发生(如果能检测,就可以再用户的代码里处理了,不用留给库去发现)。

Bjarne Stroustrup说:提供异常基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时抛出(throw)一个异常,然后它的(直接或者间接)调用者能够处理这个问题。 
The fundamental idea is that a function that finds a problem it cannot cope with throws an exception, hoping that its (direct or indirect) caller can handle the problem.

也就是《C++ primer》中说的:将问题检测问题处理相分离。 
Exceptions let us separate problem detection from problem resolution

C语言中的处理错误方式

在C语言的世界中,对错误的处理总是围绕着两种方法:一是使用整型的返回值标识错误;二是使用errno宏(可以简单的理解为一个全局整型变量)去记录错误。当然C++中仍然是可以用这两种方法的。

这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回1表示成功,返回0表示出错;而有些函数返回0表示成功,返回非0表示出错。

还有一个缺点就是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。当然,你也可以通过指针或者C++的引用来返回另外的值,但是这样可能会令你的程序略微晦涩难懂。

异常为什么好

在如果使用异常处理的优点有以下几点:

1. 函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用C语言的error宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。

2. 整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。

3. 整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。

4. 异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。

C++中使用异常时应注意的问题

任何事情都是两面性的,异常有好处就有坏处。如果你是C++程序员,并且希望在你的代码中使用异常,那么下面的问题是你要注意的。

1. 性能问题。这个一般不会成为瓶颈,但是如果你编写的是高性能或者实时性要求比较强的软件,就需要考虑了。

2. 指针和动态分配导致的内存回收问题:在C++中,不会自动回收动态分配的内存,如果遇到异常就需要考虑是否正确的回收了内存。在java中,就基本不需要考虑这个,有垃圾回收机制。

3. 函数的异常抛出列表:java中是如果一个函数没有在异常抛出列表中显式指定要抛出的异常,就不允许抛出;可是在C++中是如果你没有在函数的异常抛出列表指定要抛出的异常,意味着你可以抛出任何异常

4. C++中编译时不会检查函数的异常抛出列表。这意味着你在编写C++程序时,如果在函数中抛出了没有在异常抛出列表中声明的异常,编译时是不会报错的。而在java中会检查。

5. 在java中,抛出的异常都要是一个异常类;但是在C++中,你可以抛出任何类型,你甚至可以抛出一个整型。(当然,在C++中如果你catch中接收时使用的是对象,而不是引用的话,那么你抛出的对象必须要是能够复制的。这是语言的要求,不是异常处理的要求)。

6. 在C++中是没有finally关键字的。而java和python中都是有finally关键字的。

捕获异常

抛出异常用throw,捕获用try……catch

我们可以借助 C++ 异常机制来捕获上面的异常,避免程序崩溃。捕获异常的语法为:

try{// 可能抛出异常的语句
}catch(exceptionType variable){// 处理异常的语句
}
string str = "http://c.biancheng.net";try{
char ch1 = str[100];//数组下标越界访问cout<<ch1<<endl;
}catch(exception e){cout<<"[1]out of bound!"<<endl;
}try{char ch2 = str.at(100);cout<<ch2<<endl;
}catch(exception &e){  //exception类位于<exception>头文件中cout<<"[2]out of bound!"<<endl;
}

第一个 try 没有捕获到异常,输出了一个没有意义的字符(垃圾值)。因为[ ]不会检查下标越界,不会抛出异常,所以即使有错误,try 也检测不到。换句话说,发生异常时必须将异常明确地抛出,try 才能检测到;如果不抛出来,即使有异常 try 也检测不到。所谓抛出异常,就是明确地告诉程序发生了什么错误。

第二个 try 检测到了异常,并交给 catch 处理,执行 catch 中的语句。需要说明的是,异常一旦抛出,会立刻被 try 检测到,并且不会再执行异常点(异常发生位置)后面的语句。本例中抛出异常的位置是第 17 行的 at() 函数,它后面的 cout 语句就不会再被执行,所以看不到它的输出。

C++ 标准的异常

C++ 提供了一系列标准的异常,定义在 <exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:

下表是对上面层次结构中出现的每个异常的说明:

异常 描述
std::exception 该异常是所有标准 C++ 异常的父类。
std::bad_alloc 该异常可以通过 new 抛出。
std::bad_cast 该异常可以通过 dynamic_cast 抛出。
std::bad_exception 这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid 该异常可以通过 typeid 抛出。
std::logic_error 理论上可以通过读取代码来检测到的异常。
std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument 当使用了无效的参数时,会抛出该异常。
std::length_error 当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator[]()。
std::runtime_error 理论上不可以通过读取代码来检测到的异常。
std::overflow_error 当发生数学上溢时,会抛出该异常。
std::range_error 当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error 当发生数学下溢时,会抛出该异常。

案例:

1. 除0

#include <iostream>
using namespace std;
double division(int a, int b){if( b == 0 ){throw "Division by zero condition!";//const char*}return (a/b);
}
int main (){int x = 50;int y = 0;double z = 0;try {z = division(x, y);cout << z << endl;}catch (const char* msg) {cerr << msg << endl;}return 0;
}

由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。

2. 定义新的异常

您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:

例2:依照C++标准实现自定义异常类myException并将throw语句封装到函数check()中

涉及到的更改正如标题所述,(1)重写基类的what()函数,返回错误信息;(2)将throw myException()封装到check()函数中;(3)允许check()函数抛出myException类型的异常。代码如下:C++函数声明后面加throw()的作用!

 #include<exception>
#include<iostream>
using namespace std;  class myException:public exception{  public:  const char* what()const throw(){ //throw () 表示不允许任何异常产生 return "ERROR! Don't divide a number by integer zero.\n";  }
};
void check(int y) throw(myException){ //throw (myException)表示只允许myException的异常发生 if(y==0) throw myException();
}  int main()
{  int x=100,y=0;  try{  check(y);  cout<<x/y;  }catch(myException& me){  cout<<me.what();  }  system("pause");  return 0;
}

下面的编译也能通过:

void check(int y) throw()
{  if(y==0) throw myException();  // // 程序会在这里崩溃.(编者注:如果该异常被处理,不会崩溃)
} 

成员函数声明后面跟上throw(),表示告诉类的使用者:我的这个方法不会抛出异常,所以,在使用该方法的时候,不必把它至于 try/catch 异常处理块中。

声明一个不抛出异常的函数后,你有责任保证在你的函数的实现里面不会抛出异常。

函数后面声明 throw() 只是接口的提供者和接口的使用者间的默契或称协议。


作者:无涯明月

上篇: C++函数声明后面加throw()的作用!


C++ 异常处理(try catch)相关推荐

  1. Java 异常处理 try catch finally throws throw 的使用和解读(一)

    最近的一个内部表决系统开发过程中, 发现对异常处理还存在一些模棱两可的地方, 所以想着整理一下 主要涉及到: 1.try catch finally throws throw 的使用和解读 2.自定义 ...

  2. js的异常处理 try catch

    js的异常处理 try catch 参考文章: (1)js的异常处理 try catch (2)https://www.cnblogs.com/luxd/p/6148545.html 备忘一下.

  3. 一个存储过程帮你了解 事务(TRAN)、异常处理(TRY/CATCH)、@@ERROR

    /*  事务:begin transaction开始事务 . commit transaction 提交事务.  rollback transaction 回滚事务. SAVE TRAN 保存事务从本 ...

  4. C#异常处理try catch

    原文地址:点击打开链接 本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算提前检查不要信任外部数据可信任的设备:摄像头.鼠标以及键盘"写操作"同样可 ...

  5. 异常处理(try/catch)

    #include<stdio.h> int main() {try{printf("打印块1代码执行\n");throw 10;//产生一个异常 }catch(int& ...

  6. js中的异常处理try...catch使用介绍

    在JavaScript可以使用try...catch来进行异常处理.例如: 复制代码 代码如下: try { foo.bar();} catch (e) { alert(e.name + " ...

  7. 【转】 ABAP中的异常处理 - TRY CATCH的使用实例

    在平时的ABAP开发中,需要捕获的异常通常为两种,一种是执行SQL,比如主键重复,INSERT语句字段类型不匹配等.还有就是RFC的通信错误,比如不能进行远程连接等.通常可以这么处理: 1.数据库异常 ...

  8. 异常处理try...catch...throw

    C++ 引入了异常处理机制.其基本思想是:函数 A 在执行过程中发现异常时可以不加处理,而只是"拋出一个异常"给 A 的调用者,假定为函数 B. 拋出异常而不加处理会导致函数 A ...

  9. 小白的JAVA学习笔记(九)---异常处理(try/catch/finally,ducking)

    在编写程序的过程中不可能是一帆风顺的,我们可能会调用一些有风险的方法,也就是这些方法可能会发生异常.当我们知道调用某个方法有一定的可能性发生异常时,我们可以提前做好准备来处理问题程序.那我们怎么知道哪 ...

  10. C# 异常处理(Catch Throw)IL分析

    1.catch throw的几种形式及性能影响: private void Form1_Click(object sender, EventArgs e){try{}catch{throw;}}pri ...

最新文章

  1. NC:中国药科郝海平和郑啸发现饮食-微生物互作缓解肠损伤
  2. 牛津-阿斯利康疫苗与出血性疾病风险轻微升高有关 |《自然-医学》论文
  3. ZooKeeper基础学习
  4. Docker - Tips
  5. python pillow库_python pillow模块用法
  6. javaScript入门之常用事件
  7. 自嗨锅要持续嗨,还得碾碎这四个“绊脚石”
  8. poj 1006 java_POJ 1006 Biorhythms 数论-(孙子定理)
  9. 四中方式实现单例模式
  10. mysql 外键(foreign key)的详解和实例_MySQL数据库外键
  11. Chrome谷歌插件开发-01
  12. 012-ViewState状态保持
  13. Linux 使用root用户登录系统,并查看当前的路径。 查看当前目录下面的所有文件(包括隐藏文件)。 在当前目录下,查看根目录中的目录结构。
  14. 聚宽macd底背离_很多散户可能永远都不会知道:MACD月线金叉,每一次MACD月金叉都会带来一波牛市...
  15. 数学建模论文写作方法
  16. 基于STM32的ESP8266天气时钟(2)--------MCU获取天气数据
  17. 吉尔伯特定律(转载)
  18. SPSS聚类分析(含k-均值聚类,系统聚类和二阶聚类)
  19. Linux 系统挂载大硬盘(>2TB)及默认登录目录修改
  20. EEG信号处理与分析常用工具包介绍

热门文章

  1. 用Python抓新型冠状病毒肺炎实时数据,绘制市内疫情地图
  2. 机器学习模型应用以及模型优化的一些思路
  3. OpenWRT 镜像
  4. 7-1 求奇数分之一序列前N项和 (15 分)
  5. gdb定位Segmentation fault 问题
  6. getElementById的用法
  7. 函数C语言基础代码总结
  8. Unity一个月学习体会
  9. java springboot 汉字拼音工具类
  10. 为什么我的VS开发环境没有:解决方案资源管理器