Fork me on Github Fork me on Gitee

C++温故补缺(十一):异常

异常处理

参考:cnblogs

异常和错误(bug)

异常:是程序开发过程中必须考虑的一些特殊情况,是程序运行时可以预料的执行分支。异常是不可以避免的,如0除问题,数组越界问题,文件不存在问题等。但是可以处理,通过抛出,捕获异常,可以使程序继续运行

错误:是程序的缺陷,是程序运行时无法预料的运行方式,一旦发生错误,程序将会被终止

如:

遇到错误时:

#include<iostream>
using namespace std;

int main(){
    const int a=10;
    a++;
}

程序会终止运行

而遇到异常时,可以抛出,捕获,程序不会因为异常而终止运行,而是在抛出异常后继续运行后续代码

#include<iostream>
using namespace std;

void divide(int x,int y){
    if(y==0){
        throw -1;
    }
    cout<<x/y;
}

int main(){
    try{
        divide(20,0);
    }catch(int){
        cout<<"零除异常"<<endl;
    }
    cout<<"over"<<endl;
}

如上,在遇到异常时,throw抛出一个表达式,可以是任意类型的,但是catch时要和throw抛出的表达式类型相同,如这里抛出-1,catch就要捕获int,如果抛出了"exception"这样的字符串,catch就要捕获const char*类型。

捕获完就可以处理异常,处理异常的过程是直接跳转到catch语句后,throw后面的语句便不再执行。

如:

#include<iostream>
using namespace std;


int main(){
    try{
        cout<<"hello";
        throw -1;
        cout<<"Tom";
    }catch(int a){
        cout<<"异常:"<<a<<endl;
    }
}

throw 出异常后,其后面的输出不再执行

且如上,catch捕获的不止throw表达式类型,同时可以赋给一个变量,然后在catch块中使用

异常的基本原则

  • 通常,异常的抛出和捕获并不是在同一个函数中进行的,假设在函数B()中遇到了异常,并不会直接在B中捕获处理异常,而是抛出给它的调用者假设为A(),
B(){
    ...
    throw -1;
}
A(){
    try{
    B();
}catch(int){
    ...
}
}

如果A中没有捕获异常,程序将会自动继续向上抛出给A的调用者,一直到main()函数,如果都没有捕获异常,程序将终止执行

  • 一个try语句后可以跟多个catch语句,捕获多种类型的异常,但至少有一个catch块

  • 使用catch(...)可以捕获任意类型的异常

  • 异常只能被一个catch捕获,一旦被捕获,其他的catch就没有捕获机会了

try..catch嵌套(在catch中重新抛出异常)

try...catch可以嵌套使用,也就是内层的catch捕获到异常后什么都不做,直接抛出,由上一层的catch来处理异常,如:

#include<iostream>
using namespace std;

int main(){
    try{
        try{
            cout<<"内层抛出异常"<<endl;
            throw -1;
        }catch(int){
            throw 'a';
        }
    }catch(char){
        cout<<"外层处理异常"<<endl;
    }
}

为什么要在catch语句中重新抛出异常?

第三方库的异常解释可读性很差,甚至需要查手册才能知道什么异常,比如直接抛出一些数值,像windows系统中常见的error:-1,error:#0x0081之类的,根本不可能通过这些信息知晓异常的详情

所以在引入第三方库后,如果遇到这些异常,就捕获了重新抛出,然后加入一些自定义的解释,来增加可读性

模拟:

#include<iostream>
using namespace std;

void lib(){
    throw -1;
}

void myfunc(){
    try{
        lib();
    }catch(int a){
        if(a==-1){
            throw "参数错误";
        }
    }
}
int main(){
    try{
        myfunc();
    }catch(const char* str){
        cout<<"发生异常:"<<str<<endl;
    }
}

自定义异常类

异常类是可以自定义的,用于整个大工程,将通用的异常集合起来管理。且异常类通常也会有派生类,一般也类似一个Exception父类,派生多种不同的异常类。在赋值兼容性原则中,一般将匹配子类异常的catch放在上部,匹配父类异常的catch放在下部。

例子:

#include<iostream>
using namespace std;

class Exception{
    private:
        int Eid;
        string Edes;
    public:
        Exception(int id,string des){
            Eid=id;
            Edes=des;
        };

        int getEid(){return Eid;};
        string getEdes(){return Edes;};
};
void lib(){
    throw -1;
}

void myfunc(){
    try{
        lib();
    }catch(int a){
        if(a==-1){
            throw Exception(-1,"参数错误");
        }
    }
}
int main(){
    try{
        myfunc();
    }catch(Exception e){
        cout<<"发生异常:"<<e.getEid()<<e.getEdes()<<endl;
    }
}

C++标准库中的异常类

posted @ 2023-03-20 23:19  Tenerome  阅读(35)  评论(0编辑  收藏  举报