异常处理
1. 概念
1.1. 异常事件
1.1.1. 概念
- 程序在运行时出现错误,也叫发生了异常事件,简称 发生了异常
如果不在代码中针对可能出现的异常事件提供处理预案,程序可能无法继续正常运行,甚至崩溃。
1.1.2. 常见异常事件
除数为零
- 数组越界
- 访问空指针
- 申请内存时内存不够
示例
#include <iostream>
using namespace std;
void divide(){
cout << "请输入一个除数 : " << endl;
int n;
cin >> n;
int r = 888 / n;
cout << "r = " << r << endl;
cout << "game over" << endl;
}
int main(){
while(1){
divide();
}
return 0;
}
1.2. 异常处理
- 异常处理是C++处理异常事件的一项机制。
- 异常事件被处理后,程序可以继续运行。
-
1.3. 异常值
异常值是一个携带异常事件信息的值。
- 异常值可以是常量,变量,表达式。比如:1, e, 1+2, “hello”, A( )
1.4. 异常类型
异常值的类型,简称异常类型,可以是 基本类型,也可以是 类。
2. 异常处理语法
2.1. 形式
- C++将对异常事件的处理,转化成了对异常值的处理
throw e; // 当异常事件发生时,用throw语句抛出一个携带异常事件信息的异常值
try{
// 可能抛出异常值的代码块
}catch(异常类型 异常类型名){
// 异常处理代码,异常处理方法
}
2.2.1. 示例代码
```cppinclude
using namespace std;
void divide(){ try{ cout << “请输入一个除数 : “ << endl; int n; cin >> n; if(n == 0){ throw “除数为0”; } int r = 888 / n; cout << “r = “ << r << endl; }catch(const char* e){ cout << e << endl; //… } cout << “game over” << endl; } int main(){ while(1){ divide(); } return 0; }
<a name="mQIrA"></a>
## 2.2. throw语句
- throw e; // throw e用来抛出一个异常值e.
- 一般在发生异常事件时抛出异常值,相当于警报。
- throw用在try的{ }中, 表示throw的异常值可以被catch。
- throw用在catch的{ }中,表示throw的异常值被重新抛出给上层函数。
- throw用在try{ }和catch{ }之外,表示直接将异常值抛出给上层函数。
- 没有被当前函数的try-catch捕捉到的异常会沿着函数调用链条被自动抛给上层调用函数的try-catch,直至main函数,如果在main函数也没有捕捉到异常,则程序将因异常终止。
- throw e之后的语句将不被执行,直到e被catch.
<a name="X42dc"></a>
## 2.3. try_catch语句
<a name="gZXQQ"></a>
### 2.3.1. try语句
- 用来检测可能抛出异常值的代码块。
- try是检测throw抛出的异常值,而非异常事件。
- 异常事件发生时,如果不抛出异常值,try..catch不起作用。
- 只要抛出异常值,不管有没有异常事件发生,try..catch也会起作用
- try可以检测到用throw显式抛出的异常值,也可以检测到从下层函数中抛出的异常值。
<a name="nNCzH"></a>
### 2.3.2. catch语句
- catch语句用来捕捉try检测到的异常值并处理。当catch(类型)语句的类型和异常值类型匹配时称为捕捉到异常。
- catch(指定类型 异常变量)只能捕捉到指定类型的异常值,其中异常变量用来接收异常值。
- catch(...)可以捕捉到任意类型的异常值,通常用作在最后一个catch语句。
<a name="mBlTH"></a>
### 2.3.3. try...catch的配对规则:
- try...catch必须一起使用, catch至少要有一个,可以有多个。
- 只有捕捉到异常的catch分支会执行。
<a name="DZ1VN"></a>
## 2.4. 示例代码
```cpp
#include <iostream>
using namespace std;
void divide(){
try{
cout << "请输入一个除数 : " << endl;
int n;
cin >> n;
if(n == 0){
throw "除数为0";
}else if(n == 1){
throw 100;
}
int r = 888 / n;
cout << "r = " << r << endl;
}catch(const char* e){
cout << "const char* : " << e << endl;
throw; // 转抛catch到的异常
}catch(int e){
cout << "int : " << e << endl;
}
cout << "game over" << endl;
//throw 1;
}
int main(){
while(1){
try{
divide();
}catch(const char* e){
cout << "main : " << e << endl;
}
}
return 0;
}
3. 标准异常类
3.1. 前言
- 标准异常类的头文件主要包括
和 what()成员函数:
原型:const char* what(){return 异常信息字符串}<br /> 返回一个字符串,识别异常类型,携带异常信息
string的.at()可能抛出out_of_range类型异常。
3.3. 示例代码
```cpp
include
include
include
using namespace std;
int main(){ try{ string str = “hello world”;
cout << "size : " << str.size() << endl;
cout << str.at(10) << endl;
cout << str.at(12) << endl;
}catch(out_of_range e){
cout << e.what() << endl;
}catch(exception e){
cout << e.what() << endl;
}
return 0;
}
<a name="emJEW"></a>
# 4. 函数的异常声明
<a name="BoOC0"></a>
## 4.1. 概念
- 异常声明用来声明函数最多可能抛出的异常类型,又叫函数的异常规范。
- 异常声明会简化调用函数的异常处理代码的编写
- 如果实际抛出的异常类型超出申明的范围,则该异常将无法捕获,导致进程终止。
<a name="soxwB"></a>
## 4.2. 语法
- `函数原型 throw(异常类型列表);`
- void f() throw(int , double); //声明f最多可能抛出的int, double型异常。
- void f() throw(); //表示不会抛出异常,在c++11中用noexcept代替throw()
- void f(); //不写异常声明表示可能抛出任何类型的异常
<a name="OO0k9"></a>
## 4.3. 示例代码
```cpp
#include <iostream>
using namespace std;
void f() throw(int, double){
cout << "f()" << endl;
//throw "超出声明的异常";
throw 3.14;
throw 888;
}
void g() throw(){
cout << "g()" << endl;
}
void h(){
cout << "h()" << endl;
}
int main(){
try{
f(); // 只抛int, double异常
}catch(int e){
cout << "int : " << e << endl;
}catch(double e){
cout << "double : " << e << endl;
}catch(const char* e){
cout << "const char* : " << e << endl;
}
g(); // 不抛异常,不用异常处理
try{
h(); // 可能抛出任何类型异常
}catch(...){
cout << "..." << endl;
}
cout << "game over" << endl;
return 0;
}
5. C++异常处理的优点
- 异常处理机制允许底层函数将异常抛出给上层调用函数处理,更合理。
- 使用try-catch可以集中处理多种异常,代码更容易阅读和维护