zoukankan      html  css  js  c++  java
  • C++学习笔记(十五):异常

    C++之父Bjarne Stroustrup在《The C++ Programming Language》中讲到:一个库的作者可以检测出发生了运行时错误,但一般不知道怎样去处理它们(因为和用户具体的应用有关);另一方面,库的用户知道怎样处理这些错误,但却无法检查它们何时发生(如果能检测,就可以再用户的代码里处理了,不用留给库去发现)。
    Bjarne Stroustrup说:提供异常的基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时抛出(throw)一个异常,然后它的(直接或者间接)调用者能够处理这个问题。
    也就是《C++ primer》中说的:将问题检测和问题处理相分离。

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

    1. 性能问题。这个一般不会成为瓶颈,但是如果你编写的是高性能或者实时性要求比较强的软件,就需要考虑了。
    2. 指针和动态分配导致的内存回收问题:在C++中,不会自动回收动态分配的内存,如果遇到异常就需要考虑是否正确的回收了内存。在java中,就基本不需要考虑这个,有垃圾回收机制真好!
    3. 函数的异常抛出列表:java中是如果一个函数没有在异常抛出列表中显式指定要抛出的异常,就不允许抛出;可是在C++中是如果你没有在函数的异常抛出列表指定要抛出的异常,意味着你可以抛出任何异常。
    4. C++中编译时不会检查函数的异常抛出列表。这意味着你在编写C++程序时,如果在函数中抛出了没有在异常抛出列表中声明的异常,编译时是不会报错的。而在java中,eclipse的提示功能真的好强大啊!
    5. 在java中,抛出的异常都要是一个异常类;但是在C++中,你可以抛出任何类型,你甚至可以抛出一个整型。(当然,在C++中如果你catch中接收时使用的是对象,而不是引用的话,那么你抛出的对象必须要是能够复制的。这是语言的要求,不是异常处理的要求)。
    6. 在C++中是没有finally关键字的。而java和python中都是有finally关键字的。

    如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。
    由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。

     1 throw 表达式;
     2 
     3 try 
     4 {  
     5         包含可能抛出异常的语句;  
     6 }  
     7 catch(类型名 [形参名]) // 捕获特定类型的异常  
     8 {  
     9  
    10 }  
    11 catch(类型名 [形参名]) // 捕获特定类型的异常  
    12 {  
    13  
    14 }  
    15 catch(...)    // 三个点则表示捕获所有类型的异常  
    16 {  
    17 } 

    示例:

     1 #include<iostream.h>     //包含头文件  
     2 #include<stdlib.h>  
     3  
     4 double fuc(double x, double y) //定义函数  
     5 {  
     6     if(y==0)  
     7     {  
     8         throw y;     //除数为0,抛出异常  
     9     }  
    10     return x/y;     //否则返回两个数的商  
    11 }  
    12  
    13 void main()  
    14 {  
    15     double res;  
    16     try  //定义异常  
    17     {  
    18         res=fuc(2,3);  
    19         cout<<"The result of x/y is : "<<res<<endl;  
    20         res=fuc(4,0); 出现异常,函数内部会抛出异常  
    21     }  
    22     catch(double)             //捕获并处理异常  
    23     {  
    24          cerr<<"error of dividing zero.
    ";  
    25          exit(1);                //异常退出程序  
    26     }  
    27 } 

    标准库中的异常类:
    和java一样,标准库中也提供了很多的异常类,它们是通过类继承组织起来的。标准异常被组织成八个。
    异常类继承层级结构图如下:

    标准异常类的成员:
    在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
    logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述;
    所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。

    标准异常类的具体描述:
    exception 所有标准异常类的父类
    bad_alloc 当operator new and operator new[],请求分配内存失败时
    bad_exception 这是个特殊的异常,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为bad_exception类型
    bad_typeid 使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常
    bad_cast 使用dynamic_cast转换引用失败的时候
    ios_base::failure io操作过程出现错误
    logic_error 逻辑错误,可以在运行前检测的错误
    runtime_error 运行时错误,仅在运行时才可以检测的错误

    logic_error的子类:
    length_error 试图生成一个超出该类型最大长度的对象时,例如vector的resize操作
    domain_error 参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数
    out_of_range 超出有效范围
    invalid_argument 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常

    runtime_error的子类:
    range_error 计算结果超出了有意义的值域范围
    overflow_error 算术计算上溢
    underflow_error 算术计算下溢

    编写自己的异常类:

    1. 建议自己的异常类要继承标准异常类。因为C++中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。
    2. 当继承标准异常类时,应该重载父类的what函数和虚析构函数。
    3. 因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数。
  • 相关阅读:
    设置文本框的九种对齐方式(左上,中上,右上,左中,中中,右中,左下,中下,右下)
    VB实现小数和分数的相互转化
    已知三角形三个边的长度值,求三个角的大小
    全国专业技术人员计算机应用能力考试
    EXCELSHEET 中"输入”或“编辑”状态与“就绪”状态的切换
    递归方法巧解不定方程(二)
    VB计算圆周率
    获取路径名的原始大小写状态
    opengl NeNe 第二课的学习
    广东电信公话业务中CRM系统的研究与探索
  • 原文地址:https://www.cnblogs.com/hammerc/p/4030685.html
Copyright © 2011-2022 走看看