异常
异常提供了一种通过将控制权转移到称为
处理程序的特殊函数来响应程序中异常情况(如运行时错误)的方法。
要捕获异常,我们必须将一段代码置于异常检查之下。这是通过将该代码段封装在
try块中来完成的。当该块内出现异常情况时,会抛出一个异常,将控制权转移到异常处理程序。如果没有抛出异常,代码将正常继续,所有处理程序都将被忽略。
异常是通过在try块内部使用throw关键字来抛出的。异常处理程序使用关键字声明
catch,它必须紧跟在try块之后。
|
// exceptions
#include <iostream>
using namespace std;
int main () {
try
{
throw 20;
}
catch (int e)
{
cout << "An exception occurred. Exception Nr. " << e << endl;
}
return 0;
}
|
An exception occurred. Exception Nr. 20 |
受异常处理的代码被封装在一个
try块中。在这个例子中,这段代码只是抛出一个异常。
throw表达式接受一个参数(在本例中为整数值
20),它作为参数传递给异常处理程序。
异常处理程序使用
catch关键字声明。如您所见,它紧跟在
try块的闭合花括号之后。catch的格式类似于一个常规函数,它至少有一个参数。此参数的类型非常重要,因为throw表达式传递的参数的类型会与它进行检查,只有在它们匹配的情况下,异常才会被捕获。
我们可以链接多个处理程序(catch表达式),每个处理程序具有不同的参数类型。只有类型与throw语句中指定的参数匹配的处理程序才会被执行。
如果我们使用省略号(
...)作为
catch的参数,该处理程序将捕获任何类型的异常,而不管
throw异常的类型是什么。如果将其放在最后,可以将其用作捕获所有未被其他处理程序捕获的异常的默认处理程序。
1 2 3 4 5 6
|
try {
// code here
}
catch (int param) { cout << "int exception"; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }
|
在这种情况下,最后一个处理程序将捕获任何抛出的异常,其参数不是
int也不是
char.
。在异常被处理之后,程序执行将从
try-catch块之后恢复,而不是从
throw语句之后恢复!。
也可以将
try-catch块嵌套在更外部的
try块中。在这些情况下,我们有可能内部
catch块将异常转发到其外部级别。这是通过表达式
throw;(不带参数)来完成的。例如,
1 2 3 4 5 6 7 8 9 10 11
|
try {
try {
// code here
}
catch (int n) {
throw;
}
}
catch (...) {
cout << "Exception occurred";
}
|
异常规范
声明函数时,我们可以通过在函数声明后附加
throw后缀来限制它可能直接或间接抛出的异常类型。
1
|
float myfunction (char param) throw (int);
|
这声明了一个名为
myfunction的函数,该函数接受一个类型为
char的参数,并返回一个类型为
float的元素。此函数可能抛出的唯一异常是类型为
int的异常。如果它直接或间接抛出了不同类型的异常,则无法被常规的
int-type处理程序捕获。
如果此
throw说明符为空(没有类型),则表示函数不允许抛出异常。没有
throw说明符的函数(常规函数)可以抛出任何类型的异常。
1 2
|
int myfunction (int param) throw(); // no exceptions allowed
int myfunction (int param); // all exceptions allowed
|
标准异常
C++标准库提供了一个专门用于声明要作为异常抛出的对象的基类。它被称为
exception,定义在
<exception>头文件中,位于
std命名空间下。此类具有通常的默认和复制构造函数、运算符和析构函数,以及一个名为
what的附加虚拟成员函数,该函数返回一个以null结尾的字符序列(
char *),并且可以在派生类中重写以包含某种异常描述。
|
// standard exceptions
#include <iostream>
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main () {
try
{
throw myex;
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
|
My exception happened. |
我们放置了一个通过引用捕获异常对象的处理程序(注意类型后的&符号
&),因此这也捕获了派生自
exception的类,例如我们的
myex对象,它是
myexception.
类的实例。C++标准库的所有组件抛出的异常都派生自这个
std::exception类。它们是:
exception | 描述 |
bad_alloc | 由 new 在分配失败时抛出。 |
bad_cast | 由 dynamic_cast 在与引用类型失败时抛出。 |
bad_exception | 当异常类型不匹配任何 catch 时抛出。 |
bad_typeid | 由 typeid 抛出。 |
ios_base::failure | 由 iostream 库中的函数抛出。 |
例如,如果我们使用运算符
new并且无法分配内存,则会抛出类型为
bad_alloc的异常。
1 2 3 4 5 6 7 8
|
try
{
int * myarray= new int[1000];
}
catch (bad_alloc&)
{
cout << "Error allocating memory." << endl;
}
|
建议将所有动态内存分配包含在 try 块中,该块捕获此类异常,以执行干净的操作,而不是异常的程序终止,当此类异常被抛出且未被捕获时会发生这种情况。如果你想强制一个
bad_alloc异常来看它的实际效果,你可以尝试分配一个巨大的数组;在我的系统上,尝试分配 10 亿
int个 字节 会抛出
bad_alloc异常。
因为
bad_alloc派生自标准基类
exception,我们可以通过捕获
exception类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
// bad_alloc standard exception
#include <iostream>
#include <exception>
using namespace std;
int main () {
try
{
int* myarray= new int[1000];
}
catch (exception& e)
{
cout << "Standard exception: " << e.what() << endl;
}
return 0;
}
|
|