1 异常的概念
程序在运行过程中可能产生异常,异常与 Bug 的区别在于,异常是程序运行时可预料的执行分支,Bug 是程序中的错误,是不被预期的运行方式。下面列举几个常见的异常与 Bug。
异常
- 运行时产生除 0 的情况
- 需要打开的外部文件不存在
- 数组访问时越界
Bug
- 使用野指针
- 堆数组使用后未释放
- 选择排序无法处理长度为 0 的数组
2 C++中的异常
C++ 内置了异常处理的语法元素 try…catch…
- try 语句处理正常代码逻辑,catch 语句处理异常情况
- try 语句中的异常由对应的 catch 语句处理
C++ 通过 throw 语句抛出异常信息
举例如下:
divide 中 throw 抛出的异常会被 try…catch… 捕获
throw 抛出的异常必须被 catch 处理,当前函数能够处理异常,程序继续往下执行,当前函数函数无法处理异常,则函数停止执行,并返回。未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将被停止执行。
编程实验: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(…) 用于处理所有类型的异常
- 任何类型的异常只能被捕获一次
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 用于分割正常代码与异常代码
下面的代码写法是正确的
3.3 指定可能抛出的异常类型
- 为了提高代码可读性,在函数声明和定义时可以直接指定可能抛出的异常类型
下面的代码指出函数可能抛出的异常类型为 int
编程实验: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、异常处理必须严格匹配,不进行任何的类型转换