zoukankan      html  css  js  c++  java
  • 异常

    C++内置了异常处理语法元素 try ,catch,throw。

    try里面语句里产生异常( devide(1,0)产生除零异常),try里面函数devide用throw扔出异常值(对象,值),然后程序返回调用点,catch在调用点捕获到异常。

        try
        {
            double r = divide(1, 0);  //运行包含异常的代码
        }
        catch(...)
        {
            cout << "error" << endl;  //上面语句有异常则调用这里的语句
        }

     throw抛出的异常必须被catch处理。当前函数能够处理异常,则程序往下执行。当前函数无法处理异常,函数停止执行并异常返回(没有返回值)。

     没有被处理的异常顺着函数调用栈向上传播,直至异常被处理,如果没有处理异常则整个程序停止。

    例:

     

     在函数3中发生异常,用关键字throw抛出异常给try,try再传递异常给catch。如果没有try和catch这样异常处理语句,throw则返回异常值1给上一个函数2,

     函数2,1也做同样的工作,如果找不到异常处理语句,程序停止在函数1。

    除零异常:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    double divide(double a, double b)
    {
        if( b == 0)
        {
            throw 0; 
        }
        else
        {
            return  a / b;
        }
    }
    
    int main(int argc, char *argv[])
    {    
        try
        {
            divide(1, 0);
        }
        catch(...)
        {
            cout << "error" << endl;
        }
        return 0;
    }

     规则:

      一个try语句块可以跟上多个catch语句。

      catch语句可以定义具体处理的异常类型。

        不同类型是我异常由不同的catch语句负责。

      try语句可以抛出任何类型的异常。

      catch(...)用于处理任意类型的异常。

      任何异常都只能被捕获(catch)一次 。

     异常处理的匹配规则:

    异常由throw抛出异常值后,由try传给catch,再由每个catch严格匹配异常值类型(不会进行类型转化),如果不匹配则调用下一个catch,如果程序没有匹配的catch则停止运行。

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    void Demo1()
    {
        try
        {   
            throw 'c';
        }
        catch(char c)
        {
            cout << "catch(char c)" << endl;  // 不进行类型转化,一个异常只能被一个catch所捕获
        }
        catch(short c)
        {
            cout << "catch(short c)" << endl; // 不进行类型转化
        }
        catch(double c)
        {
            cout << "catch(double c)" << endl; // 不进行类型转化
        }
        catch(...)
        {
            cout << "catch(...)" << endl;  // 用于接收任意类型的异常,只能放在所有catch最后一个的位置
        }
    }
    
    int main(int argc, char *argv[])
    {    
        Demo1();   // catch(...)
        return 0;
    }

    catch相当于函数的调用,参数严格匹配的那种

    catch语句也可以抛出异常:

        try  //  外层异常处理语句
        {
            try    // 内层异常处理语句
            {
                throw 1;
            }
            catch(int i)  // 内层catch中捕获异常值为int类型后处理异常或抛出异常
            {
                throw i;   
            }
            catch(...)  // 内层catch捕获任意异常后处理或则抛出
            {
                throw;
            }
        }
        catch(int i)  //外层catch中捕获异常后处理内层异常或再抛出异常
        {
         
        }

    catch抛出异常的意义:统一异常类型。

    当用私有库封装第三方库时,第三方库的函数func()的异常类型为short,对应的封装函数为my_func()的异常类行为int,可以在func()中抛出short类型的异常值,在函数my_func()中用catch接住异常并重新抛出int类型的异常值,这样就可以统一异常值类型了。

    示例如下:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    void lib_func(int i)  // 三方库函数
    {
        try
        { 
            throw i;
        }
        catch(int i)
        {
            if(i < 0)
                throw -1;  // 不做处理,直接抛出异常
            if(i > 0)
                throw 1;
            if(i == 0)
                throw 0;
        }
    }
    
    void my_func(int i)   // 自己库函数
    {
        try
        {
            lib_func(i);
        }
        catch(int i)   
        {
            switch(i)  // 对于异常的重新解释
            {
                case  1:
                    throw "Timeout Exception";
                    break;
                case  0:
                    throw "Runtime Exception";
                    break;
                case -1:
                    throw "Invalid Parameter";
                    break;
            }
        }
    }
    
    int main(int argc, char *argv[])
    {
        try
        {
            my_func(0);
        }
        catch(const char* cs)
        {
            cout << "Exception Info: " << cs << endl;
        }
        return 0;
    }

    类类型的异常值:

    自上而下严格匹配,子类的异常对象可以被父类catch语句块所抓住(子类初始化父类)(赋值兼容性原则)。匹配子类异常的catch放在上面,匹配父类异常的catch放在下面,防止子类被父类捕获。

    用自定义类类型来描述异常,实现如下:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Base
    {
    };
    
    class Exception : public Base    //自定义异常类
    {
    public:
        int m_id;
        string m_desc;
        Exception(int id, string desc)
        {
            m_id = id;
            m_desc = desc;
        }
    };
    
    void lib_func(int i)  // 三方库函数
    {
        try
        {
            throw i;  
        }
        catch(int i)
        {
            if(i < 0)
                throw -1;  
            if(i > 0)
                throw 1;
            if(i == 0)
                throw 0;
        }
    }
    
    void my_func(int i)  // 自己的库函数
    {
        try
        {
            lib_func(i);
        }
        catch(int i)   
        {
            switch(i)  // 对于三方库异常的重新解释
            {
                case  1:
                    throw Exception(0,"Timeout Exception");
                    break;
                case  0:
                    throw Exception(1,"Runtime Exception");
                    break;
                case -1:
                    throw Exception(-1,"Invalid Parameter");
                    break;
            }
        }
    }
    
    int main(int argc, char *argv[])
    {
        try
        {
            my_func(0);
        }
        catch(const Exception& e)    // 打印异常类的信息。使用引用,防止调用拷贝构造函数
        {
            cout << "Exception Info: " << " ID:  " << e.m_id << " Description:  " << e.m_desc << endl;
        }
        catch(const Base& e)   // 子类可以初始化父类,这里也可以调用
        {
            cout << "catch(const Base& e)" << endl;
        }
        return 0;
    }

    C++标准库含有异常类族

    exception类,logic_error类,runtime_error类

    logic_error:可以避免的错误(空指针,下标越界)

    runtimer_error:无法避免的错误(内存溢出)

    main()函数中抛出异常:

    主函数中有无法处理的异常时,terminate()函数会被自动调用,terminate()继续调用abort()函数中止程序并异常退出,C++支持替换默认的terminate()函数。

    terminate()替换函数:

      1. 定义一个无参无返回值的函数:

      2. 不能抛出任何异常

      3. 结束当前进程

    调用set_terminate定义terminate()替换函数:  

      1. 参数类型为void(*)()

      2. 返回值为默认的termainate()函数入口地址

    #include <iostream>
    #include <cstdlib>
    #include <exception>  // 异常处理头文件
    
    using namespace std;
    
    void my_terminate()
    {
        cout << "my_terminate()" << endl;
        exit(1);  // 结束当前进程
    }
    
    class Test 
    {
    public:
        Test() 
        {
            cout << "Test()"; 
            cout << endl;
        }
        
        ~Test() 
        {
            cout << "~Test()"; 
            cout << endl;
        }
    };
    
    int main()
    {
        set_terminate(my_terminate);  // 设置自定义中止函数 
        static Test t;
        throw 1;
        return 0;
    }

    异常声明:

    声明函数所抛出的异常。异常声明时函数声明的修饰符,写在参数列表后面。异常规格说明是函数接口的一部分。

    void func();                             // 可能抛出任何异常

    void func(); throw(int, char);   // 可能抛出的异常是int类型的异常值或char类型的异常值

    void func(); throw();                // 不抛出任何异常

    函数抛出的异常值类型不在异常说明里面时,全局函数unexcepected()函数被调用,然后默认的unexcepected()函数会调用全局terminate()函数。(可用自定义的函数替换unexcepected()函数)

    unexcepected()函数的替换:

      1. 能够再次抛出异常,当异常符合触发函数的异常规格说明时,程序恢复执行。否则调用全局terminate()结束进程。

      2. 调用set_unexcepected()函数设置自定义的unexcepected()函数。

      3. 自定义的unexcepected()函数参数类型是void(*)(),返回值是unexcepected()函数的入口。

    #include <iostream>
    #include <cstdlib>
    #include <exception>
    
    using namespace std;
    
    void my_unexpected()
    {
        // exit(1);
        throw 1;
    }
    
    void func() throw(int)
    {
        throw 'c';      // 抛出的异常不符合异常规格,将调用自定义的my_unexpected函数,
    }                    // 在my_unexpected函数中又抛出为int类型异常值,异常值兼容异常规格,程序恢复执行。
    
    int main()
    {
        set_unexpected(my_unexpected);
        try 
        {
            func();  // 相当于只发生了int的异常
        } 
        catch(int)  // 可以只声明了异常的类型,并没有使用标识符来表示捕获异常
        {
            cout << endl;
        } 
        catch(char) 
        {
            cout << endl;
        }
        return 0;
    }
  • 相关阅读:
    最小生成树计数
    Tree
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ Physics
    NOJ Physics
    NOJ Physics
    NOJ Physics
  • 原文地址:https://www.cnblogs.com/zsy12138/p/10864169.html
Copyright © 2011-2022 走看看