异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。

异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw。

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
  • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
// 保护代码
throw 抛出异常
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

抛出异常

可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。以下是尝试除以零时抛出异常的实例:

1
2
3
4
5
6
double division(int a, int b) {
if( b == 0 ) {
throw "Division by zero condition!"; // 异常类型是string
}
return (a/b);
}

捕获异常

catch 块跟在 try 块后面,用于捕获异常。可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

1
2
3
4
5
6
7
try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}

上面的代码会捕获一个类型为 ExceptionName 的异常。如果想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:

1
2
3
4
5
6
7
try
{
// 保护代码
}catch(...)
{
// 能处理任何异常的代码
}

下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

double division(int a, int b){
if( b == 0 ) {
throw "Division by zero condition!";
}
return (a/b);
}

int main () {
int x = 50;
int y = 0;
double z = 0;

try {
z = division(x, y); // 异常在division函数中抛出
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}

由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:

Division by zero condition!

C++ 标准的异常

C++ 提供了一系列标准的异常,定义在 中,我们可以在程序中使用这些标准的异常。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream> 
#include <string>
#include <stdexcept>
int main() {
std::string str("foo");
try {
str.at(10); // access element, may throw std::out_of_range
} catch (const std::out_of_range& e) {
// what() is inherited from std::exception and contains an explanatory message
std::cout << e.what();
}
}

连续多个异常捕获

1
2
3
4
5
6
7
8
9
10
11
std::string str("foo");    
try {
str.reserve(2); // reserve extra capacity, may throw std::length_error
str.at(10); // access element, may throw std::out_of_range
} catch (const std::length_error& e) {
std::cout << e.what();
} catch (const std::out_of_range& e) {
std::cout << e.what();
} catch (const std::exception& e) {
std::cout << e.what();
}

what() 是 length_error/runtime_error… 类的一个成员函数,每个标准库异常类都定义了名为 what 的成员函数。what() 函数没有参数,返回值是 C 风格字符串(即 const char *)其中, what 成员函数返回的是初始化一个具体对象所用的 string 对象的副本。

定义自己的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <stdexcept>
// or #include <exception>
using namespace std;

struct MyException : public exception {
const char * what () const throw () {
return "C++ Exception";
}
};

int main() {
try {
throw MyException();
}
catch(MyException& e) {
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
}
catch(std::exception& e) {
//其他的错误
}
}

exception 还是 stdexcept

标准c++库定义了两个与异常相关的库,。异常通常用来报告错误。

exception定义了与异常处理相关的类,声明和函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class exception{
public:
exception() throw();
exception(const char *const &);
exception(const char *const &, int);
exception(const bad_exception &ob) throw();
virtual ~exception() throw();
exception &operator=(const exception &ob) throw();
virtual const char *what(() const throw();
};

class bad_exception : public exception {
public:
bad_exception() throw();
bad_exception(const bad_exception &ob) throw();
virtual ~bad_exception() throw();
bad_exception &operator=(const bad_exception &ob) throw();
virtual const char *what(() const throw();
};

exception类是c++库中所有异常的父类。unexpected()函数抛出的就是bad_exception类型。每个异常类都重写了what()用于返回一段描述异常的字符串。

一些重要的类都继承于exception类。第一个是bad_alloc,当new操作失败时被抛出。另一个是bad_typeid,当错误地使用typeid运算时抛出。

移步参考其他文章