c++的异常处理

1. c++的异常处理介绍

  c++的异常处理语法:

int main(void)
{
    try
    {
        double a = div(6, 0);
    }
    catch(...)
    {
        printf("div 0 is error!!\n");
    }   
    return 0;
}

  如上try…catch两个语句块,将正常功能代码和异常处理代码在一个函数中分隔开,提高代码的可读性:try语句用于处理正常代码逻辑,catch语句用于处理异常情况,try语句中的异常由对应的catch语句处理。

  在div()函数中,若发现除以0操作,可以通过throw语句抛出异常,并终止本函数运行:

double div(double a, double b)
{
    double tmp = 0.00000001;

    if ((-tmp < b) && (b < tmp))
    {
        throw 0;
    }
    else
        return a / b;
}

  编译运行:
这里写图片描述

  在div()函数中抛出的异常必须被catch处理,当前函数能够处理异常,程序则继续往下执行,当前函数无法处理异常,则函数停止执行并返回。未被处理的异常会顺着函数调用栈向上抛出,直至被处理为止,
这里写图片描述

  若直到main()函数都没有处理,那么程序将停止执行:

int main(void)
{
    //try
    //{
        double a = div(6, 0);
    //}
    //catch(...)
    //{
    //  printf("div 0 is error!!\n");
    //}

    return 0;
}

  编译运行:
这里写图片描述

  一个函数执行过程中可能会抛出多重类型的异常,那么也就意味着,一个try语句可以跟上多个catch。catch语句可以定义具体处理的异常类型,不同的类型的异常由不同的catch语句负责处理。代码中的catch(…)可用于处理所有类型的的异常,需要注意,任何异常都只能被捕获一次。

void exception_test1()
{
    try{
        throw 'a';
    }

    catch(char c)
    {
        printf("catch(char c)\n");
    }

    catch(int i)
    {
        printf("catch(int i)\n");
    }

    catch(double d)
    {
        printf("catch(double d)\n");
    }

    catch(...)
    {
        printf("catch(...)\n");
    }
}

int main(void)
{
    exception_test1();
    return 0;
}

  编译运行:
这里写图片描述

  异常抛出后,编译器将自上而下严格匹配每一个catch语句处理的类型,异常处理会进行严格类型匹配,不会进行任何类型转换:

void exception_test2()
{
    throw std::string("hello");
}

int main(void)
{
    try
    {
        exception_test2();  
    }
    catch(char* s)
    {
        printf("catch(char* s)\n");
    }
    catch(const char* cs)
    {
        printf("catch(char* s)\n");
    }
    catch(std::string ss)
    {
        printf("catch(std::string ss)\n");
    }   

    return 0;
}

  编译运行:
这里写图片描述

2. catch语句块抛出异常

  前面讲到try语句块中可能会抛出异常,抛出的异常被当前函数或者函数调用栈的上一和函数的catch语句块处理。在catch语句块中,处理异常操作还可以是把异常再次抛出,catch抛出的异常需要外层的try…catch语句捕获。

  在catch没有抛出异常的情景中:

void test_func(int i)
{
    switch (i)
    {
        case 1:
            throw -1;
            break;

        case 2:
            throw -2;
            break;

        case 3:
             throw -3;
             break;

        default:
            break;
    }
    printf("void test_func(int i)\n");
}

int main(void)
{
    try
    {
        test_func(2);
    }
    catch(int i)
    {
        printf("catch(int i), i = %d\n", i);
    }
    return 0;
}

  编译运行:
这里写图片描述
  抛出异常为-2,此时对于程序调试者可能还尚未知道-2究竟是什么错误码。然而它并不知道test_func(int i)的源代码,只能通过另外的查询手段去获取-2是什么异常,所以说比较麻烦。在实际工程中,catch语句块捕获到的异常可以被重新解释后抛出,假设:
  -1代表输入错误
  -2代表通讯异常
  -3代表其它错误

  上面函数不动,增加run_test_func()函数:

void run_test_func(int i)
{
    try{
        test_func(i);
    }
    catch (int i)
    {
        if (i == -1)
        {
            throw "input error!";
        }
        else if (i == -2)
            throw "communication erroe!";
        else if ( i == -3)
            throw "error!";
    }
}

  在main()函数中,try语句块调用run_test_func()函数,catch语句块捕获run_test_func()可能抛出的异常:

int main(void)
{
    try
    {
        run_test_func(2);
    }
    catch(const char* s)
    {
        printf("%s\n", s);
    }
    return 0;
}

  编译运行:
这里写图片描述
  这样就能对程序所抛出的异常类型一目了然。

3. 抛出类类型异常

  异常的类型还可以是自定义的类类型,对于类类型的异常匹配规则依旧是从上而下严格匹配,但是注意,子父类的赋值兼容性原则在异常匹配中仍然适用,所以说匹配子类异常的 catch应该放在上面,匹配父类异常的chtch放在下面。

//异常的基类
class exceptionBase
{
public:
};

class exceptionSub : public exceptionBase
{
private:
    int m_id;
    std::string desc_str;

public:
    exceptionSub(int id, std::string str) : m_id(id), desc_str(str)
    {}

    int id()
    {
        return m_id;
    }

    std::string description()
    {
        return  desc_str;
    }
};

void test_func(int i)
{
    switch (i)
    {
        case 1:
            throw -1;
            break;

        case 2:
            throw -2;
            break;

        case 3:
             throw -3;
             break;

        default:
            break;
    }

    printf("void test_func(int i)\n");
}

void run_test_func(int i)
{
    try{
        test_func(i);
    }
    catch (int i)
    {
        if (i == -1)
        {
            //throw "input error!";
            throw exceptionSub(-1, "input error!");
        }
        else if (i == -2)
            //throw "communication erroe!";
            throw exceptionSub(-2, "communication erroe!");
        else if ( i == -3)
            //throw "error!";
            throw exceptionSub(-3, "error!");
    }
}

int main(void)
{
    try
    {
        run_test_func(2);
    }
    catch(exceptionSub& ee)
    {
        printf("ID: %d\n", ee.id());
        printf("description: %s\n", ee.description().c_str());
    }
    catch(exceptionBase& ee)            //因为支持赋值兼容性原则,所以父类类型应放在子类类型后面
    {
        printf(" catch(exceptionBase& ee)\n");
    }
    return 0;
}

  编译运行:
这里写图片描述

4. try…catch的两种特殊写法

  (1) c++的函数声明和定义时可以直接指定可能抛出的异常类型,异常声明成为函数声明的一部分。这可以提高代码的可阅读性,如:

void func1(int i) trow(int) //表示该函数可能抛出int类型的异常
{
    //...
}

void func2(int i, char c)   //表示该函数可能抛出int类型、char类型的异常
{
    //...
}

  需要注意的是,函数的异常声明是一种和编译器的协议,也就是说函数声明异常后就只能抛出所声明的类型的异常。

void func(int i) throw(int)
{
    if (i > 5)
        throw -i;
}

int main(void)
{
    try{
        func(9);
    }
    catch(int i)
    {
        printf("exception: %d\n", i);
    } 
    catch(...)
    {
        printf("catch(...)\n");
    } 

    return 0;
}

  编译运行正常:
这里写图片描述

  尝试在声明抛出异常类型为int的func()函数中抛出char类型的异常:

void func(int i) throw(int)
{
    if (i > 5)
        throw 'e';
}

  编译运行:
这里写图片描述

  即使是在main()函数中有catch(…)代码用于捕捉所有异常,但是不起作用,func()函数违背了与编译器的协议了。

  (2) try…catch的意义在于用于分隔正常功能代码与异常功能代码,这个分隔实现还可以写成这样:

void test_fun(int i) try
{
    func(i);
    printf("test_fun...\n");
}
catch(int i)
{
    printf("test_fun, catch(int) = %d\n", i);
}
catch(...)
{
     printf("test_fun, catch(...)\n");
}

int main(void)
{
    test_fun(12);
    test_fun(4);

    return 0;

  编译运行:
这里写图片描述
  这样的写法会让初学者感觉try…catch处于两个函数一样,其实不然,它们跟前面写的在一个函数内实现的效果是一致的。

  c++的异常先笔记到这里,下来看看c++标准库的异常类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值