异常(Exceptions)
异常提供了一种通过将控制转移到名为处理程序的特殊功能来对我们程序中异常情况(如运行时错误)做出反应的方法。
要捕获异常,我们必须将一部分代码放在异常检查下。 这是通过将该部分代码包含在try块中来完成的。 当该块中出现异常情况时,抛出异常,将异常处理程序传递给控件。如果没有抛出异常,代码将继续正常运行,所有处理程序都将被忽略。
使用try块内的throw关键字抛出异常。异常处理程序用关键字catch声明,它必须在try块之后立即放置。异常处理下的代码包含在try块中。
下面我们分别解释一下这三个keyword:
- try :块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。当该块中出现异常情况时,抛出异常,将异常处理程序传递给控件。
- throw:当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。引发异常
- catch:英文直译为捕获,用于声明异常处理程序,当try语句中的特定的异常代码标识被触发时,执行这里的代码,因此紧跟try后面。
throw使用示例
throw 语句在代码块中的可以在任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。比如我们都知道的常识,被除数不能为0,如果用户输入的话我们可以直接抛出异常,我们很自然可以想到在 if 语句后面加 throw。
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a / b);
}
但是这样的运行我们肯定有疑问,因为throw不是cout,这里的函数返回类型是double不是我们的字符串,所以我们的直接throw是不会有反应的,编译器会编译出错。我们可以写个代码试试:
#include <iostream>
using namespace std;
double division(int a, int b);
int main(){
double z = division(4,0);
cout << z;
return 0;
}
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a / b);
}
运行结果是这样的:
那么是不是我们用法有问题?当然不是!等会我们就来解决这个问题!
try, catch 的配对使用
常规的使用格式如下:
try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}
ExceptionName 指的是异常的类型,可以为int型 也可以为 double,char等等,例如下面的一个简单的例子:
#include <iostream>
using namespace std;
int main () {
try
{
throw 20;
}
catch (int e) //ExceptionName 的类型是int,注意我们这个时候并没有用throw而是cout
{
cout << "An exception occurred. Exception Nr. " << e << endl;
}
return 0;
}
运行结果:
异常处理程序用catch关键字声明。你可以看到,它立即跟随try块的关闭括号。 捕获格式类似于始终至少有一个参数的常规函数。此参数的类型非常重要,因为通过throw表达式传递的参数的类型与之匹配,只有在匹配的情况下,才会捕获异常。(这就是为什么我们前面会报错)
我们可以链接多个处理程序(catch表达式),每个处理程序具有不同的参数类型。 只有匹配其类型的处理程序才能执行在throw语句中指定的参数。
如果我们使用省略号(…)作为catch的参数,该处理程序将捕获任何异常,无论抛出异常的类型是什么。这可以用作一个默认处理程序,它捕获最后指定的其他处理程序未捕获的所有异常:
try {
// 代码
}
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来完成的,没有throw的内容。例如:
try {
try {
// code here
}
catch (int n) {
throw;
}
}
catch (...) {
cout << "Exception occurred";
}
现在我们来解决上面的throw问题:
#include <iostream>
using namespace std;
int main(){
while(true){
double a, b;
cin >> a >> b;
//在这里 try里面放置的是可能出现异常的代码,我们也称为保护代码
try{
if(b == 0) throw "抛出异常,被除数为0";
double z = a / b;
cout << z << endl;
}catch(const char* msg){ /*catch紧跟在try后面,我们抛出的异常是个字符串
*所以为const char类型的指针,我们抛出的异常是
*被储存在名为msg的字符数组中,然后用char型指针
*逐个输出我们抛出的内容
*/
cerr << msg << endl;
}
}
return 0;
}
当我们的输入的数字中,被除数不为0的时候,程序就不会执行try-catch语句,而是正常运行,当触发时,catch语句就捕获这类的错误,抛出我们的内容:
多catch程序示例
下面我们来看一段测试代码:
/*
*这段代码是我为了深入体会 try - catch语句所写
*展示的原理是,我们catch的内容是指的我们抛出的内容类型
*这里我们输入一个char值,然后我们判断各种类型,抛出不同的异常
*最后实现多个catch的不同反应
*/
#include <iostream>
#include <cctype>
using namespace std;
int main(){
while(true){
char b;
cin >> b;
try{
if(isdigit(b)) throw 0;
if(isupper(b)) throw 'a';
if(islower(b)) throw " lower char";
/*下面的代码根据抛出的不同的异常类型,做出相应的输出*/
}catch(int e){
cout << "int exception" << endl;
}catch(char e){
cout << "char exception" << endl;
}catch(...){ //因为我们没有定义catch字符串类型的异常所以省略号会帮我们完成这部分处理
cout << "default exception" << endl;
}
}
return 0;
}
运行结果如下: