异常

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;
}
原文地址:https://www.cnblogs.com/zsy12138/p/10864169.html