C++:异常

一,错误处理(传统办法)

(1)终止程序。(如段错误)
(2)返回错误码。
(3)返回合法值,让程序处于某种非法状态。
(4)调用一个预先设置的出现错误时调用函数(回调函数)

除了以上传统的错误处理技术,异常处理机制是一种比较有效的处理系统运行时错误的方法。C++针对异常处理提供了一种标准的方法,用于处理软件程序运行时的错误,并用于处理软件系统中可预知或不可预知的问题。这样就可以保证软件系统运行的稳定性与健壮性。
二,异常处理
异常使得我们能够将问题的检测与解决过程分离开来。当一个函数发现自己无法处理的错误抛出异常,让函数的调用者直接或间接的处理这个问题。
三,抛出异常与捕获
当执行一个throw时,跟在throw后面的语句将不再执行,程序的控制权从throw转移到与之匹配的catch模块,控制权从一处转移到另一处,有两重意义:沿着调用链的函数可能会提早退出;一旦程序开始执行异常处理代码,沿着调用链创建的对象将被销毁,(类似与return)。

#include<iostream>
using namespace std;
int Divide(int left,int right)
{
    if(right==0)
    {
        throw 1;//throw:当分母为0时,抛出一个异常
    }
    return left/right;
}
int main()
{
    try//try块中放置可能抛出异常的代码
    {
        cout<<"12/4="<<Divide(12,4)<<endl;
        cout<<"16/8="<<Divide(16,8)<<endl;
        cout<<"6/0="<<Divide(6,0)<<endl;
    }
    catch(...)//捕获异常
    {
        cout<<"分母不能为0"<<endl;
    }
    return 0;
}

这里写图片描述
注:在catch语句中,使用三个点(…)。即写成:catch (…) 这里三个点是“通配符”,类似 可变长形式参数。
四,栈展开
抛出异常的时候,将暂停当前函数的执行,开始查找对应匹配的catch子句。
首先检查throw本身是否在catch块内部,如果是再查找匹配的catch语句。如果有匹配的,则处理,没有则退出当前函数栈,继续在调用函数的栈中进行查找。不断重复以上过程,若到达main函数的栈依旧没有匹配的,则终止程序。
找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。

栈展开过程沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch子句为之;或者也可能一直没找到匹配的catch,则退出主函数后查找过程终止。

void f1(int i)
{
    if (i<0)
    throw 1;
}
void f2()
{
    f1(-9);
}
void f3()
{
    f2();
}
int main()
{
    try{
        f3();
    }
    catch (int i)
    {
        cout <<"error code:"<< i <<"\n"<< endl;
    }
    system("pause");
    return 0;
} 

这里写图片描述
调用链:
这里写图片描述
五,捕获异常的匹配规则:
异常对象的类型与catch说明符的类型必须完全匹配。只有以下几种情况例外:(1)允许从非const对象到const的转换
(2)允许从派生类到基类类型的转换。
(3)将数组转换为指向数组类型的指针,将函数转换为指向函数的指针

六,异常与构造函数&析构函数
(1)构造函数完成对象的构造和初始化,需要保证不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化.
(2)析构函数主要完成资源的清理,需要保证不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等).

七,自定义异常类型
(1)为什么要编写自己的异常类?

① 标准库中的异常是有限的;
② 在自己的异常类中,可以添加自己的信息。(标准库中的异常类值允许设置一个用来描述异常的字符串)。

(2)如何编写自己的异常类?

① 建议自己的异常类要继承标准异常类。因为C++中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。
② 当继承标准异常类时,应该重载父类的what函数和虚析构函数。
③ 因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数

举例:

#include<iostream>
#include<exception>
using namespace std;
class Exception : public exception//继承标准异常类
{
public:
    Exception(int errId = 0,const char* errMsg = "")
        :_errId(errId)
        ,_errMsg(errMsg){}
public:
    virtual const char* what()const
    {
        cout<<"errId:"<<_errId<<endl;
        cout<<"errMsg:"<<_errMsg.c_str()<<endl;
        return _errMsg.c_str();
    }
private:
    int _errId;
    string _errMsg;
};
void TestException()
{
    throw Exception(1,"错误!");//抛出异常类
}
int main()
{
    try
    {
        TestException();
    }
    catch(exception &e)
    {
        e.what();
    }
}   

这里写图片描述
八,总结
优点:
(1)不像错误码那么模糊,明确描述清楚了错误。
(2)很多的其它语言和库都使用异常,可以更好地兼容。
(3)能更好的进行测试。
缺点:
(1)执行流乱跳,不方便调试。
(2)效率会变低,抛出异常后查找catch子句,相比返回错误码效率有影响。
(3)异常安全的场景下,需要RAII支撑。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值