1 异常的概念

程序在运行过程中可能产生异常,异常与 Bug 的区别在于,异常是程序运行时可预料的执行分支,Bug 是程序中的错误,是不被预期的运行方式。下面列举几个常见的异常与 Bug。

异常

  • 运行时产生除 0 的情况
  • 需要打开的外部文件不存在
  • 数组访问时越界

Bug

  • 使用野指针
  • 堆数组使用后未释放
  • 选择排序无法处理长度为 0 的数组

2 C++中的异常

C++ 内置了异常处理的语法元素 try…catch…

  • try 语句处理正常代码逻辑,catch 语句处理异常情况
  • try 语句中的异常由对应的 catch 语句处理

C++ 通过 throw 语句抛出异常信息

举例如下:
【C++深度解析】46、C++ 中的异常处理(上)_c  学习
divide 中 throw 抛出的异常会被 try…catch… 捕获

throw 抛出的异常必须被 catch 处理,当前函数能够处理异常,程序继续往下执行,当前函数函数无法处理异常,则函数停止执行,并返回。未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将被停止执行。
【C++深度解析】46、C++ 中的异常处理(上)_C++深度解析_02

编程实验:C++异常处理初探

// 46-1.cpp
#include <iostream>
#include <string>
using namespace std;
double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    if ( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
    }
    else
    {
        throw 0;
    }
    return ret;
}
int main(int argc, char *argv[])
{
    try
    {
        double r = divide(1, 0);
        cout << "r = " << r << endl;
    }
    catch(...)
    {
        cout << "Divide by zero..." << endl;
    }
    return 0;
}

程序首先执行 try 内中的代码,divide 函数中的 throw 抛出异常,该函数中没有 catch 捕获异常,返回到 main 函数中,被 catch 捕获。

编译运行:

$ g++ 46-1.cpp -o 46-1
$ ./46-1
Divide by zero...

3 try…catch…详解

3.1 一个 try 跟多个 catch

同一个 try 语句可以跟上多个 catch 语句。

  • catch 语句可以定义为具体处理的异常类型,不同类型的异常由不同的 catch 语句负责处理
  • try 语句中可以抛出任何类型的异常
  • catch(…) 用于处理所有类型的异常
  • 任何类型的异常只能被捕获一次

【C++深度解析】46、C++ 中的异常处理(上)_C++深度解析_03
catch(…) 必须要放在最后,自上而下要严格匹配,包括 const 属性。

编程实验:异常类型匹配

// 46-2.cpp
#include<iostream>
using namespace std;
void  Demo1()
{
    try
    {
        throw 'c';
    }
    catch(char c)
    {
        cout << "catch(char c)" << endl;
    }
    catch(short c)
    {
        cout << "catch(short c)" << endl;
    }
    catch(double c)
    {
        cout << "catch(double c)" << endl;
    }
    catch(...)
    {
        cout << "catch(...)" << endl;
    }
}

void Demo2()
{
    throw "error";
}
int main()
{
    Demo1();
    try
    {
        Demo2();
    }
    catch(char* s)
    {
        cout << "catch(char* s)" << endl;
    }
    catch(const char* cs)
    {
        cout << "catch(const char* cs)" << endl;
    }
    catch(string ss)
    {
        cout << "catch(string ss)" << endl;
    }
	return 0;
}

函数 Demo1 中的异常在函数中被 catch 捕获,有四个 catch,分别捕获 char,short,double,和其他所有类型。

Demo2 中的异常返回到 main 函数中才被捕获,异常处理必须严格匹配,包括 const 属性,抛出的异常 “error” 被 catch(const char* cs) 捕获。要被 catch(string ss) 捕获,需要 throw string(“error”);

$ g++ 46-2.cpp -o  46-2
$ ./46-2
catch(char c)
catch(const char* cs)

3.2 try…catch 用于分割

  • try…catch 用于分割正常代码与异常代码

下面的代码写法是正确的
【C++深度解析】46、C++ 中的异常处理(上)_c  学习_04

3.3 指定可能抛出的异常类型

  • 为了提高代码可读性,在函数声明和定义时可以直接指定可能抛出的异常类型

下面的代码指出函数可能抛出的异常类型为 int

【C++深度解析】46、C++ 中的异常处理(上)_C++深度解析_05
编程实验:try…catch分割代码,throw 指定异常类型

// 46-1.cpp
#include<iostream>
using namespace std;

int func(int i, int j) throw(int, char)		// 指定可以抛出int和char异常
{
    if (0 < j && j < 10)
    {
        return i + j;
    }
    else
    {
        throw '0';
    }
}

void test(int i) try				// try…catch 用于分割正常代码与异常代码
{
    cout << "func(i, i) = " << func(i, i) << endl;
}
catch (int i)
{
    cout << "Exception: " << i << endl;
}
catch (...)
{
    cout << "Exception..." << endl;
}

int main(int argc, char* argv[])
{
    test(4);
    test(10);
    return 0;
}

编译运行:

func(i, i) = 8
Exception...

4 小结

1、try…catch…是 C++ 中异常处理的专用语句
2、try 语句处理正常代码逻辑,catch 语句处理异常代码逻辑
3、同一个 try 可以跟多个 catch 语句
4、异常处理必须严格匹配,不进行任何的类型转换