异常的概念
程序遇到了问题非主动退出的情况下我们称之为发生了异常,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。
c++中异常的处理方式:

#include
using namespace std;
void test1()
{
    FILE *fp = fopen("2222.txt", "rb");
    if (fp == nullptr){
        throw 1;
    }
}

int main()
{
    try
    {
        test1();
    }
    catch (int)
    {
        cout << "文件打开失败" << endl;
    }
    system("pause");
    return 0;
}

即通过throw抛出一种异常,由catch捕获,try调用函数(可以理解为有可能抛出异常,所以尝试着调用这个函数)

异常的抛出和捕获规则:
1.异常是通过抛出对象所触
2.被选中的处理代码是与抛出异常对象类型匹配且距离最近的那个
3.抛出异常会生成一个异常对象的拷贝,是一个临时对象,所以会在catch后被销毁
4.特殊:并不一定是类型完全匹配,有时可以抛出一个派生类对象,使用基类捕获,这个很常用也很实用


在异常的使用和捕获中遵循栈展开匹配原则:
首先检查throw本身是否在try内部,如果在的话找到与之匹配的catch,如果没有与之匹配的catch则退出当前函数栈,继续在调用当前函数的函数栈中找与之匹配的catch,找到后就从catch后继续执行代码,如果一直找到main函数依旧没有找到的话就终止程序。


异常的重现抛出:有时接收到异常的地方无法处理异常,而是需要将异常进行一些矫正处理后交给更外层的调用函数去处理异常,这里就需要catch(...)重新抛出异常。

#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;

int Division(int a, int b)
{
    if (b == 0){
        throw "Division error";
    }
    return a / b;
}

void Fun()
{
    int * arr= new int[10];
    try
    {
        int ret = Division(10, 0);
    }
    catch (...)
    {
        delete arr;
        throw;
    }
}

int main()
{
    try
    {
        Fun();
    }
    catch (const char* errmsg)
    {
        cout << errmsg << endl;
    }
    system("pause");
    return 0;
}

上面的代码中Division抛出的异常首先由调用它的函数Fun捕获,但是并没有处理这个异常而是将这个异常继续抛出到主函数中进行处理。


异常安全问题:最好不要在构造函数或析构函数中使用异常,否则可能会导致对象构造不完全或析构不彻底。C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题。


异常规范:

int Division(int a, int b) throw(int,char)
{
    if (b == 0){
        throw "Division error";
    }
    return a / b;
}

通过这种方式告诉函数使用者此函数有可能抛出的异常类型,如果函数后是throw()则表示这个函数不会抛出异常。


最后总结一下异常的优缺点
优点:更清晰的展示错误的各种信息,以及调用堆栈的信息
缺点:使用异常会使程序跳动性比较大,执行混乱
但利大于弊,工程中鼓励使用异常处理。