【转】编程思想之异常处理

什么叫异常处理?

什么叫异常(Exception)?顾名思义就是非正式的情况,出现了不希望出现的意外,异常处理就是遇到这种意外时准备的对策和解决方案。比如您开着一辆劳斯莱斯在公路上行走,突然前面出现一个小孩,幸好您眼疾手快紧急刹车,而避免了一场交通事故。在这个例子中突然出现的小孩就是异常,紧急刹车就是异常处理(面对这种突发情况采取的解决方案)。

程序来源于现实,是现实的抽象和模拟,也会有异常,因此异常的处理就显得极为重要。试想如果您的手机的某个应用使用两下就崩溃了,或都出现各种意想不到情况,您是不有种想扔手机的冲动,您还会再用这种App吗?

异常处理的解决方案

C++中的异常处理:

C++中的异常处理主要有两种实现方式:(1).返回错误码,(2)try...catch机制捕获异常。

返回错误码

返回错误码是传统的C语言的处理异常的方式。在一个函数中,如果发生某种不该发生的错误或异常,则直接返回一个错误码,函数的调用方在调用该函数的时候根据返回的错误码的类型进行相应的处理。由于C++的历史原因(由C发展而来),为了兼容性,现在的C++程序中仍能看到很多用错误码的方式向上抛出异常信息。

这种方式一般用于对性能的要求较高的地方,常用在系统库和接口的实现中。因此这种方式可以精确控制逻辑,让程序员只关注逻辑和性能,而程序的完整性和健壮性,交给上层调用方去处理。其实现方式如下:

例【1】 对两个浮点数进行除法运算,如果除数为0则抛出错误码。

1、定义错误码 RetCode.h

 1 #ifndef RETCODE_H
 2 #define    RETCODE_H
 3
 4 typedef long ReturnCode;
 5
 6 //成功
 7 #define RT_OK                       0L
 8 //失败
 9 #define RT_FAILED                   1L
10
11 //参数错误
12 #define RT_PARAM_ERROR              2L
13 //无法预知的错误
14 #define RT_UNEXPECTED_ERROR         3L
15 //空指针
16 #define RT_NULL_PTR                 4L
17 //分配内存错误
18 #define RT_ALLOCATE_MEMORY_FAILED   5L
19 //不支持的操作
20 #define RT_UNSUPPORT_OPERATE        6L
21
22
23 #endif    /* RETCODE_H */

错误码的实现方式:

 1 #include "RetCode.h"
 2 #include <iostream>
 3
 4 //判断一个浮点数是否为0
 5 #define DEQUALZEOR(X)  ((X) <= 0.0001 && (X) > -0.0001)
 6
 7 //除法运算
 8 ReturnCode division(double dividend, double divisor, double& result)
 9 {
10     if(DEQUALZEOR(divisor))
11         return RT_PARAM_ERROR;
12     else
13     {
14         result = dividend / divisor;
15         return RT_OK;
16     }
17 }
18
19 int main(int argc, char** argv)
20 {
21     double r = 0;
22     ReturnCode ret = division(5, 0, r);
23     if(RT_PARAM_ERROR == ret)
24     {
25         std::cout << "参数错误,检查是否除数为0。" << std::endl;
26     } else
27     {
28         std::cout << r << std::endl;
29     }
30     return 0;
31 }

try...catch机制捕获异常

上面这种返回错误码的方式,可能有效地定义和控制各种错误和异常,但是每个调用都要检查错误值,极不方便,也容易让程序规模加倍。其实C++有一种专门的机制用于处理异常,那就是try...catch机制。

try
{
        // 抛出异常,或可能抛出异常的调用
} catch (ExceptioinObject e)
{
        // 处理异常
} catch (...)
{
        // 捕获所有类型的异常
}

说明:

1.try中的代码块用于抛出(throw)异常,或调用可能抛出异常的函数、对象;

2.throw关键字可用于抛出任意类型的对象,可以是类的对象,也可以是内置数据类型的对象(常称为变量),还可以是指针(指针本身就是一个对象,是一个特殊的对象,用于指向另外一个对象的地址);

3.第一个catch括号中的e表示异常对象,这个对象也可以是任意类型的对象。当throw出的对象类型与e的类型相同时,则捕获到异常,进行catch代码块中的异常处理。

4.第二个catch括号中的“...”表示任意类型,可以捕获任意类型的异常。

5.一个try可以对应一个或多个catch,catch子句被检查的顺序与它们在try块之后排列顺序相同,一旦找到一个匹配,则后续的catch子句将不再检查,按此规则,catch_all(catch(...){})表示处理前面所列各种异常之外的异常。

6.catch子句可以包含返回语句(return),也可不包含返回语句。包含返回语句,则整个调用函数结束,后面的语句不再执行。而不包含返回语句,则执行catch列表之后的下一条语句。

异常处理,把正常逻辑和错误处理分离开来,由函数实现方抛出异常,由调用者捕获这个异常,调用者就可以知道程序函数调用出现错误了,并去处理,而是否终止程序就把握在调用者手里了。

我们将用上面的例子用try...catch...方式实现

例【2】对两个浮点数进行除法运算,如果除数为0则抛出异常。

 1 #include <iostream>
 2 //判断一个浮点数是否为0
 3 #define DEQUALZEOR(X)  ((X) <= 0.0001 && (X) > -0.0001)
 4
 5 using namespace std;
 6
 7 //除数不为0异常
 8 class DivisorZeorException{
 9 public:
10     DivisorZeorException(double value) : m_value(value){}
11
12     void showInfo()
13     {
14         cout << "the divisor " << m_value << " is wrong." << endl;
15     }
16 private:
17     double m_value;
18 };
19
20 double division(double dividend, double divisor)
21 {
22     if ( DEQUALZEOR(divisor) )
23     {
24         throw DivisorZeorException(divisor);
25     }
26     return dividend / divisor;
27 }
28
29 int main()
30 {
31     try
32     {
33         double result = division(10, 0);
34         cout << "result: " << result << endl;
35     } catch (DivisorZeorException e)
36     {
37         e.showInfo();
38     } catch(...)
39     {
40         cout << "all exception" << endl;
41     }
42     return 0;
43 }

异常的高级特性

(1)异常规范

可在函数的后面用throw列出可能抛出的异常,并保证该函数不会抛出任何其它类型的异常。如上面的division函数改为:

double division(double dividend, double divisor) throw(DivisorZeorException)
{
    if ( DEQUALZEOR(divisor) )
    {
        throw DivisorZeorException(divisor);
    }
    return dividend / divisor;
}

如果在运行时,函数抛出了一个没有被列在它的异常规范中的异常(并且函数中所抛出的异常,没有在该函数内部处理),则系统调用C++标准库中定义的函数unexpected()。如果异常规范形式为throw(),则表示不得抛出任何异常。

(2)异常类的继承

异常类也可以继承,在catch捕获异常的时候应按照由子类到父类的顺序,因此catch子句被检查的顺序与它们在try块之后排列顺序相同,所以在catch子句列表中最特化的(匹配条件最严格的,即子类)catch子句必须先出现 。假设有三个异常类,ExceptionC是ExceptionB的子类,ExceptionB是ExceptionA的子类,try...catch...就写成:

try
{
    //可能抛出异常的语句
}
catch (ExceptionC c)
{
    //处理ExceptionC异常
}
catch (ExceptionB b)
{
    //处理ExceptionB异常
}
catch (ExceptionA a)
{
    //处理ExceptionA异常
}

C++标准中的异常类

C++标准中已经定义了一套常用的异常类,它们之间的层次关系如下:

exception是所有异常类的父类,仅仅定义了拷贝构造函数、拷贝赋值运算符、一个虚析构函数和一个名为what的虚成员,what函数返回一个const char*,用于返回一些异常信息。

C++标准中定义的类虽然不多,但我们在定义自己的异常类的时候还是应该尽量利用已有的异常类,至少就继承自exception类,保持结构的统一性。

转自luoweifu 《编程思想之异常处理》

时间: 2024-10-02 22:24:10

【转】编程思想之异常处理的相关文章

编程思想之多线程与多进程(2)——线程优先级与线程安全

原文:http://blog.csdn.net/luoweifu/article/details/46701167 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础.本文将接着讲一下线程优先级和线程安全. 线程优先级 现在主流操作系统(如Windows.Linux.Mac OS X)的任务调度除了具有前面提到的时间片轮转的特点外,还有优先级调度(Prior

编程思想之多线程与多进程(4)——C++中的多线程

<编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,<编程思想之多线程与多进程(2)--线程优先级与线程安全>一文讲了线程安全(各种同步锁)和优先级,这是多线程学习必须了解的基础.本文将接着讲一下C++中多线程程序的开发.这里主要讲Windows平台线程的用法,创建线程要调用windows API的CreateThread方法. 创建线程 在Windows平台,Windows API提供了对多线程的支持.前面进程和

编程思想之多线程与多进程(2)——Java中的多线程

原文:http://blog.csdn.net/luoweifu/article/details/46673975 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础.本文将接着讲一下Java中多线程程序的开发 单线程 任何程序至少有一个线程,即使你没有主动地创建线程,程序从一开始执行就有一个默认的线程,被称为主线程,只有一个线程的程序称为单线程程序.如下面

编程思想之消息机制

编程思想之消息机制 消息机制 从一个剧情开始 路遥的<平凡的世界>因为翻拍成电视剧,又再次火起来了!我们就从这里开始吧,其小说是以这样一个场景开头的: 在一个半山腰县立高中的大院坝里,在一个校园内的南墙根下,按班级排起了十几个纵队的年轻男女,各班的值日生正忙碌地给众人分发饭菜-- 菜分为甲.乙.丙三等,甲菜以土豆.白菜.粉条为主,还有可人大肉片,乙菜没有肉,丙菜只有清水煮白萝卜.主食也分为三等:白面馍,玉米面馍,高粱面馍,白.黄.黑分别代表了三种差别,学生们戏称欧洲.亚洲.非洲.每个人的饭菜都

编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程

什么是线程 什么是线程?线程与进程与有什么关系?这是一个非常抽象的问题,也是一个特别广的话题,涉及到非常多的知识.我不能确保能把它讲的话,也不能确保讲的内容全部都正确.即使这样,我也希望尽可能地把他讲通俗一点,讲的明白一点,因为这是个一直困扰我很久的,扑朔迷离的知识领域,希望通过我的理解揭开它一层一层神秘的面纱. 任务调度 线程是什么?要理解这个概念,须要先了解一下操作系统的一些相关概念.大部分操作系统(如Windows.Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执

[转帖]编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程

编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程原创luoweifu 发布于2015-06-22 20:05:28 阅读数 75442 收藏展开 原文:http://blog.csdn.net/luoweifu/article/details/46595285 作者:luoweifu 转载请标名出处 其实我还有一个不太清楚的地方 一个进程 应该只能存在于一个核上面吧 一个进程的多个线程 应该不能跨越CPU的核心进行工作吧? 不太明白. 什么是线程什么是线程?线程与进程与有什么关系

异常笔记--java编程思想

开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出异常.抛出异常后发生的几件事: 1.在堆上创建异常对象. 2.当前的执行路径中止                                          3. 当前环境抛出异常对象的引用.                                         4. 异常处理机制接

c++编程思想(一)--对象导言

回过头来看c++编程思想第一章,虽然只是对c++知识的一个总结,并没有实质性知识点,但是收获还是蛮多的! 下面感觉是让自己茅塞顿开的说法,虽然含义并不是很准确,但是很形象(自己的语言): 1.类描述了一组具有相同特性(数据元素)和相同行为(功能)的对象,本质就是一个数据类型,已存在的数据类型只是表示计算机的存储单位,定义新的类就是为了与问题结合,解决问题-----不要把类想的很复杂,直接抽象成这样 2.类要产生工作,会有一个接口,来实现功能 3.程序猿分为类创建者和客户程序猿(调用别人的类).客

《Java编程思想(第4版)》pdf

下载地址:网盘下载 内容简介 编辑 本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形.从Java的基础语法到最高级特性(深入的面向对象概念.多线程.自动项目构建.单元测试和调试等),本书都能逐步指导你轻松掌握.[1] 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作.本书的作者拥有多年教学经验,对C.C++以及Java语言都有独到.深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概