一、先讨论异常被引发后,可能导致的问题

意外异常

  如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配,否则为意外异常。在默认情况下,这将导致程序异常终止(虽然C++11摒弃了异常规范,但仍支持它,且有些现有的代码使用了它)。

未捕获异常

  如果异常不是在函数中引发的,则必须捕获它。如果没被捕获(在没有try块或没有匹配的catch块时,将出现这种情况),则异常被称为未捕获异常。

在默认情况下,这两种异常将导致程序异常终止。当然可以修改程序对意外异常和未捕获异常的反应。

==========================================================================

未捕获异常不会导致程序立刻异常终止。相反,程序将首先调用函数terminate()。在默认的情况下,terminate()调用abort()函数。

可以指定terminate()应调用的函数来修改terminate()的这种行为。

为此,可以调用set_terminate()函数。

set_terminate()和terminate()都是在头文件exception中声明的。

typedef void (* terminate_handler) ();

terminate_handler set_terminate(terminate_handler f) throw();  //C++98

terminate_handler set_terminate(terminate_handler f) noexcept;    //C++11

void terminate();                   //C++98

void terminate() noexcept;  //C++11

假设希望未捕获的异常导致程序打印一条消息,然后调用exit()函数,将退出状态值设置为5。

首先,请包含头文件exception。可以使用using编译指令、适当的using声明或std::限定符,来使其声明可用。

#include <exception>

using namespace std;

然后,设计一个完成上述两种操作所需的函数,其原型如下:

void myQuit()

{

cout<<"Terminating due to uncaught exception\n";

exit(5);

}

最后,在程序的开头,将终止操作指定为调用该函数

set_terminate(myQuit);

现在,如果引发了一个异常且没有被捕获,程序将调用terminate(),而后者将调用MyQuit()。

==========================================================================

接下来看一下意外异常

通过给函数指定异常规范,可以让函数的用户知道要捕获哪些异常。

假设函数的原型如下:

double Argh() throw(out_if_bounds);

则可以这样使用该函数:

try {

x = Argh(a,b);

}

catch(out_of_bounds & ex)

{

...

}

知道应捕获哪些异常很有帮助,因为默认情况下,未捕获的异常将导致程序异常终止。

原则上,异常规范应包含函数调用的其他函数引发的异常。例如,如果Argh()调用了Duh()函数,而后者可能引发retort对象异常,则Argh()和Duh()的异常规范中斗应包含retort。

除非自己编写所有的函数,并且特别仔细。否则无法保证上述工作都已正确完成。

所以这也表明异常规范机制处理起来比较麻烦,这也是C++11将其摒弃的原因之一。

  在这种情况之下,行为与未捕获异常极其相似。如果发生意外异常,程序将调用unexpected()函数,后者在默认情况下将调用abort()。

正如有一个可用于修改terminate()的行为的set_terminate()函数一样。

也有一个可用于修改unexpected()行为的set_unexpected()函数。这些新函数也是在头文件中exception中声明的:
typedef void(* unexpected_handler) ();

unexpected_handler set_unexpected(unexpected_handler f) throw();   //C++98

unexpected_handler set_unexpected(unexpected_handler f) noexcept;    //C++11

void unexpected();                       //C++98

void unexpected() noexcepted;    //C+0X

然而,与提供给set_terminate()函数的行为相比,提供给set_unexpected()的函数的行为受到更严格的限制。具体地说,unexpected_handler函数可以:

1、通过调用terminate()(默认行为)、abort()或exit()来终止程序;

2、引发异常。

引发异常的结果取决于unexpected_handler函数所引发的异常以及引发意外异常的函数的异常规范;

1、如果新引发的异常与原来的异常规范匹配,则程序将从那里开始进行正常处理,即寻找与新引发异常匹配的catch块。基本上,这种方法将用预期的异常取代意外异常。

2、如果新引发的异常与原来的异常规范不匹配,且异常规范中没有包括std::bad_exception类型,则程序将调用terminate()、bad_exception是从exception派生而来的,其声明位于头文件exception中。

3、如果新引发的异常与原来的异常规范不匹配,且异常规范中包括std::bad_exception类型,则不匹配的异常将被std::bad_exception异常所取代。

总之,要捕获所有的异常(不管是预期的异常还是意外异常),则可以这样做:

首先确保异常头文件的声明可用:

#include <exception>

using namespace std;

然后,设计一个替代函数,将意外异常转换为bad_exception异常,该函数的原型如下:

void myUnexpected()

{

throw std::bad_exception();

}

仅使用throw,而不指定异常将导致重新引发原来的异常。然而,如果异常规范中包含了这种类型,则该异常将被bad_exception对象所取代。

接下来在程序的开始位置,将意外异常操作指定为调用该函数:

set_unexpected(myUnexpected);

最后,将bad_exception类型包括在异常规范中,并添加如下catch块序列:

double Argh() throw(out_of_bounds, bad_exception);

...

try {

x = Argh(a,b);

}

catch(out_of_bounds & ex)

{

...

}

catch(bad_exception & ex)

{

...

}

二、有关异常的注意事项

从前面关于如何使用异常的讨论可知,应在设计程序时就加入异常处理功能,而不是以后再添加。

这样做有些缺点。

例如,使用异常会增加程序代码,降低程序的运行速度。

异常规范不适用于模板,因为模板函数引发的异常可能随特定的具体化而异。

异常和动态内存分配并非总能协同工作。

下面进一步讨论动态内存分配和异常。

void test1(int n)

{

string mesg("I'm trapped in an endless loop");

...

if (oh_no)

throw exception();

return;

}

string类采用动态内存分配。通常,当函数结束时,将为mesg调用string的析构函数。

虽然throw语句过早地终止了函数。但它仍然使得析构函数被调用,这要归功于栈解退。

因此在这里,内存被正确地管理。

接下来看这个函数:

void test2(int n)

{

double * ar = new double[n];

....

if (oh_no)

throw exception();

...

delete [] ar;

return;

}

这里有个问题,解退栈的时候,将删除栈中的变量ar。但函数过早地终止意味着函数末尾的delete[]语句被忽略。

指针消失了,但它指向的内存块未被释放,并且不可访问。总之,这些内存被泄漏了。

当然这种泄漏是可以被避免的。例如,可以再引发异常的函数中捕获该异常,在catch块中包含一些清理代码,然后重新引发异常:

void test3(int n)

{

double * ar = new double[n];

try {

}

catch (exception & ex)

{

delete [] ar;

throw;

}

...

delete [] ar;

return;

}

但是这样做,仍然会增加疏忽和产生其他错误的机会。另一种解决方法是使用智能指针模板

总之,虽然异常处理对于某些项目极为重要,但它也会增加编程的工作量,增大程序,降低程序的速度。

另一方面,不进行错误检查的代价可能非常高。

======================================================

三、异常处理

现代库中,异常处理的复杂程度可能再创新高。

理解库中的异常处理像学习语言本身一样困难。

现代库中包含的例程和模式可能像C++语法细节一样陌生而困难。

要开发出优秀的软件,必须花时间了解库和类中的复杂内容,就像必须花时间学习C++本身一样。

通过库文档和源代码了解到的异常和错误处理细节将使程序员和他的软件受益。

C++_异常9-异常的注意事项的更多相关文章

  1. _00020 妳那伊抹微笑_谁的异常最诡异第一期之 SqlServer RSA premaster secret error

    博文作者:妳那伊抹微笑 博客地址:http://blog.csdn.net/u012185296 博文标题:_00020 妳那伊抹微笑_谁的异常最诡异第一期之 SqlServer RSA premas ...

  2. 重学c#系列——异常续[异常注意事项](七)

    前言 对上节异常的补充,也可以说是异常使用的注意事项. 正文 减少try catch的使用 前面提及到,如果一个方法没有实现该方法的效果,那么就应该抛出异常. 如果有约定那么可以按照约定,如果约定有歧 ...

  3. Java异常处理-----非运行时异常(受检异常)

    非运行时异常(受检异常) 如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错. 1:IOException 使用要导入包import java.io.IO ...

  4. Python档案袋(异常与异常捕获 )

    无异常捕获 程序遇到异常会中断 print( xxx ) print("---- 完 -----") 得到结果为: 有异常捕获 程序遇到异常会进入异常处理,并继续执行下面程序 tr ...

  5. 两种异常(CPU异常、用户模拟异常)的收集

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 两种异常(CPU异常.用户模拟异常)的收集  文章的核心:异常收集 ...

  6. 异常概念&异常体系和异常分类

    异常概念 异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象.Java处 ...

  7. 第25章 SEH结构化异常处理_未处理异常及向量化异常

    25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...

  8. python进阶八_警告和异常

    心情有点纠结,怎么说呢,倒不是由于其它学习上的事情,反而是由于生活上狗血的剧情逼着人偏离,渐行渐远,人跟人之间有误会也是正常的,可能是由于交流不够,彼此不够了解吧,希望能尽快度过这一段纠结的日子,简单 ...

  9. 《Python编程从入门到实践》_第十章_文件和异常

    读取整个文件 文件pi_digits.txt #文件pi_digits.txt 3.1415926535 8979323846 2643383279 下面的程序打开并读取整个文件,再将其内容显示到屏幕 ...

随机推荐

  1. leetcode:First Missing Positive分析和实现

    题目大意: 传入整数数组nums,求nums中未出现的正整数中的最小值.要求算法在O(n)时间复杂度内实现,并且只能分配常量空间. 分析: 一般碰到这种问题,都先对数组进行排序,再遍历数组就可以找到最 ...

  2. C# 把一个文件夹下所有文件复制到另一个文件夹下 把一个文件夹下所有文件删除(转)

    C# 把一个文件夹下所有文件复制到另一个文件夹下   public static void CopyDirectory(string srcPath, string destPath) { try { ...

  3. APP前端开发时应注意的一些问题

    在做APP前端开发时应注意的一些问题 在整个app开发流程中,app前端开发是一个必不可少的环节,也是一个在app开发过程中重量级的角色.说到这,那么在app应用的前端开发中,又要注意什么问题呢?一. ...

  4. 数字图像处理实验(14):PROJECT 06-01,Web-Safe Colors 标签: 图像处理MATLAB 2017-05-27 20:45 116人阅读

    实验要求: Objective: To know what are Web-safe colors, how to generate the RGB components for a given jp ...

  5. Luogu 3616 富金森林公园

    刚看到此题的时候:sb分块??? Rorshach dalao甩手一句看题 于是回去看题……果然是题读错了…… [思路] 对权值离散化后(要先读入所有输入里的权值一起离散化……所以一共有4e4个数据( ...

  6. python之连接oracle数据库

    环境: windows,python2.7 1.下载cx_Oracle 在windows下不要使用easy_install或者pip,因为这样安装不会同步环境,并报错: distutils.error ...

  7. 不用EL表达式---实现product页面显示

    产品页面显示 静态页面如下: <%@ page language="java" contentType="text/html; charset=UTF-8" ...

  8. Requests接口测试(三)

    一.定制请求头 我们先来看一下,关于请求头的定制演示,相信了解http协议的小伙伴应该对请求头部headers请求头部并不陌生,那么作为实际工作中的我们,如果想自定义一些请求头的信息,我们应该怎么办呢 ...

  9. web端测试点汇总

    前言 前面一篇文章讲解了app测试一些功能点.那么相应的也梳理一下web测试相关的功能的测试点吧,此篇文章只是给你们一个思路,如果要涉及web端每个测试点,基本不可能实现的,所以只是提供一个设计的思路 ...

  10. 编写高质量代码改善C#程序的157个建议——建议23:避免将List<T>作为自定义集合类的基类

    建议23:避免将List<T>作为自定义集合类的基类 如果要实现一个自定义的集合类,不应该以一个FCL集合类为基类,反而应扩展相应的泛型接口.FCL结合类应该以组合的形式包含至自定义的集合 ...