异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。
异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw。
- throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
- catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
- try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:
try
{
// 保护代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}
如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。
一、抛出异常
您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。
以下是尝试除以零时抛出异常的实例:
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
二、捕获异常
catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。
try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}
上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:
try
{
// 保护代码
}catch(...)
{
// 能处理任何异常的代码
}
下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。
三、实例
#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);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
四、C++标准的异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3s96O72-1664626573614)(/home/lovess/biji/cpp/picture/14-1.png)]
五、定义新的异常
您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:
#include <iostream>
#include <exception>
using namespace std;
struct MyException : public std::exception
{
const char * what ()
{
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)
{
//其他的错误
}
}
六、异常规范和C++11
有时候,一种理念看似有前途,但实际的使用效果并不好。一个这样的例子是异常规范( exceptioni specification),这是C++98新增的一项功能,但C+11 却将其摒弃了。这意味着C++11仍然处于标准之中,但以后可能会从标准中剔除因此不建议您使用它。
他张这个样子
double marm(double) throw();
其中的throw()部分就是异常规范,它可能出现在函数原型和函数定义中,可包含类型列表,也可不包含。
异常规范的作用之一是,告诉用户可能需要使用try 块。然而,这项工作也可使用注释轻松地完成。异常规范的另一个作用是,让编译器添加执行运行阶段检查的代码,检查是否违反了异常规范。这很难检.查。例如marm( )可能不会引发异常,但它可能调用一个函数,而这个函数调用的另一个函数引发了异常。另外,您给函数编写代码时它不会引发异常,但库更新后它却会引发异常。总之,编程社区(尤其是尽力编写安全代码的开发人员)达成的一致意见是,最好不要使用这项功能。而C++11也建议您忽略异常规范。
七、异常何时会迷失方向
异常被引发后,在两种情况下,会导致问题。首先,如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配(在继承层次结构中,类类型与这个类及其派生类的对象匹配),否则称为意外异常(unexpected exception)。在默认情况下,这将导致程序异常终止(虽然C++11摒弃了异常规范,但仍支持它,且有些现有的代码使用了它)。如果异常不是在函数中引发的(或者函数没有异常规范),则必须捕获它。如果没被捕获(在没有try 块或没有匹配的catch 块时,将出现这种情况),则异常被称为未捕获异常(uncaught exception)。在默认情况下,这将导致程序异常终止。
在现代库中,异常处理的复杂程度可能再创新高——主要原因在于文档没有对异常处理例程进行解释或解释得很蹩脚。任何熟练使用现代操作系统的人都遇到过未处理的异常导致的错误和问题。这些错误背后的程序员通常面临一场艰难的战役,需要不断了解库的复杂性:什么异常将被引发,它们发生的原因和时间,如何处理它们,等等。
程序员新手很快将发现,理解库中异常处理像学习语言本身一样困难,现代厍中巴含的例在和候式可能像C++语法细节一样陌生而困难。要开发出优秀的软件,必须花时间了解库和类中的复杂内容,就像必须花时间学习C++本身一样。通过库文档和源代码了解到的异常和错误处理细节将使程序员和他的软件受益。