zoukankan      html  css  js  c++  java
  • [转贴] 从零开始学C++之异常(二):程序错误、异常(语法、抛出、捕获、传播)、栈展开

    一、程序错误

    编译错误,即语法错误。程序就无法被生成运行代码。
    运行时错误

    不可预料的逻辑错误

    可以预料的运行异常

    例如:

    动态分配空间时可能不会成功

    打开文件可能会失败

    除法运算时分母可能为0

    整数相乘可能溢出

    数组越界……


    二、异常

    (一)、异常语法

    throw  表达式;

    try
    {
       //try语句块
    }
    catch(类型1  参数1)
    {
       //针对类型1的异常处理
    }
    catch (类型2  参数2)
    {
       //针对类型2的异常处理
    }

    catch (类型n  参数n)
    {
       //针对类型n的异常处理
    }


    (二)、异常抛出

    可以抛出内置类型异常也可以抛出自定义类型异常
    throw抛出一个类对象会调用拷贝构造函数
    异常发生之前创建的局部对象被销毁,这一过程称为栈展开


    (三)、异常捕获

    一个异常处理器一般只捕捉一种类型的异常
    异常处理器的参数类型和抛出异常的类型相同
    …表示可以捕获任何异常


     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    #include <iostream>
    #include <string>
    using namespace std;

    class MyException
    {
    public:
        MyException(const char *message)
            : message_(message)
        {
            cout << "MyException ..." << endl;
        }
        MyException(const MyException &other) : message_(other.message_)
        {
            cout << "Copy MyException ..." << endl;
        }
        ~MyException()
        {
            cout << "~MyException" << endl;
        }

        const char *what() const
        {
            return message_.c_str();
        }
    private:
        string message_;
    };
    double Divide(double a, double b)
    {
        if (b == 0.0)
        {
            MyException e("division by zero");
            throw e;
            /*throw MyException("division by zero");*/
            //throw 1;
        }
        else
            return a / b;
    }
    int main(void)
    {
        try
        {
            cout << Divide(5.00.0) << endl;
        }
        catch (MyException &e)
        {
            cout << e.what() << endl;
        }
        //catch (int)
        //{
        //  cout<<"int exception ..."<<endl;
        //}
        catch (double)
        {
            cout << "double exception ..." << endl;
        }
        catch (...)
        {
            cout << "catch a exception ..." << endl;
        }
    }



    程序自定义一个异常类型MyException,从输出可以看出,Divide函数内先构造一个MyException对象e,调用构造函数,因为e是局部对象需要被析构,在析构前先调用拷贝构造函数构造另一个对象,这个对象将被catch 引用,最后这个对象在catch末尾也将被析构。

    假设没有构造局部对象,直接throw , 如 throw MyException("division by zero"); 那么将不会调用拷贝构造函数,只存在一个对象,在catch的末尾被析构。

    假设throw 1; 而没有对应的catch (int) ,即使存在catch (double) 也捕获不到,不会做类型转换,此时会由catch (...) 捕获到,...表示可以捕获任何异常。


    (四)、异常传播

    1、try块可以嵌套
    2、程序按顺序寻找匹配的异常处理器,抛出的异常将被第一个类型符合的异常处理器捕获
    如果内层try块后面没有找到合适的异常处理器,该异常向外传播,到外层try块后面的catch块中寻找
    3、没有被捕获的异常将调用terminate函数,terminate函数默认调用abort终止程序的执行
    可以使用set_terminate函数指定terminate函数在调用abort之前将调用的函数


     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    void MyTerminate()
    {
        cout << "MyTerminate ..." << endl;
    }

    int main(void)
    {
        set_terminate(MyTerminate);
        try
        {
            try
            {
                throw MyException("test exception");
            }
            catch (int)
            {
                cout << "Inner ..." << endl;
                cout << "catch a int exception" << endl;
            }
            //catch (MyException& e)
            //{
            //  cout<<"Inner ..."<<endl;
            //  cout<<e.what()<<endl;
            //  throw e;
            //}
        }
        catch (int)
        {
            cout << "Outer ..." << endl;
            cout << "catch a int exception" << endl;
        }
        catch (MyException &e)
        {
            cout << "Outer ..." << endl;
            cout << e.what() << endl;
        }
    }



    其中MyException类如上,程序中将内层的catch (MyException& e) 屏蔽了,所以由外层的catch (MyException& e) 捕获,假设将两个都注释掉的话,因为没有找到合适的catch, 那么terminate 函数会被调用,并且由于事先set_terminate 函数设定了abort调用之前被调用的函数MyTerminate,故先输出MyTerminate ...然后程序被终止。


    三、栈展开

    沿着嵌套调用链接向上查找,直至为异常找到一个catch子句。这个过程称之为栈展开。

    为局部对象调用析构函数

    析构函数应该从不抛出异常

    栈展开期间会执行析构函数,在执行析构函数的时候,已经引发的异常但还没处理,如果这个过程中析构函数又抛出新的异常,将会调用标准库的terminate函数。

    异常与构造函数

    构造函数中可以抛出异常。如果在构造函数函数中抛出异常,则可能该对象只是部分被构造。即使对象只是被部分构造,也要保证销毁已构造的成员。(如果成员是指针p,因为析构函数不会被调用,故不会执行一般的delete p; 很可能造成内存泄漏)


  • 相关阅读:
    值得 Web 开发人员学习的20个 jQuery 实例教程
    15款优雅的 WordPress 电子商务网站主题
    Red Pen
    经典网页设计:无缝过渡的响应式设计案例
    值得 Web 开发人员收藏的20个 HTML5 实例教程
    mysql分库 分表
    MySQL大数据量快速分页实现
    mysql全局唯一ID生成方案(二)
    使用 SendARP 获取 MAC 地址(使用SendARP API函数,很多相关文章)
    锁是用来解决并发问题
  • 原文地址:https://www.cnblogs.com/redmondfan/p/3194174.html
Copyright © 2011-2022 走看看