----------------siwuxie095
一般的语法知识都是在正常状态下使用的,而 异常处理
则是在非正常状态下使用的
异常处理
有人可能会问,在 C++ 中什么是异常,又如何进行处理?
所谓 异常,就是程序在运行过程当中所出现的错误
显然,错误并不是我们所希望看到的,但是在某些情况下,
这种错误却会出现,出现后就必须要处理
不处理,就会出现程序崩溃的情况,而崩溃这种情况更是
我们不愿意看到的
对程序进行异常处理,其实就是对可以预见的错误进行
合理的安排
如果做出的安排合理,并能够给出人性化的提示,使用者
就不会觉得突兀
使用者就会根据提示做相应的操作,如:网线没插好,
或 内存不足,这样使用者就会非常容易的接受你给他
的提示,并根据提示使得程序能顺利的继续向下运行
如果有些异常没有预料到,那么就会直接抛给系统
系统是很粗暴的,它会将程序直接杀死,对于用户来说,
所看到的现象就是程序直接崩溃掉,而崩溃是我们最不
愿意看到的
程序频繁地崩溃,对于开发者来说,简直就是一场噩梦
如何来进行异常处理呢?
需要用到关键字:try … catch … 和 throw
try 即 尝试运行正常的逻辑,如果在运行正常逻辑时,
出现异常,就会通过 catch 将其捕获,捕获之后,再
去对出现的异常进行处理
throw 即 抛出,具体到这里,即 抛出异常,抛出之后,
被 catch 捕获,捕获之后,再进行处理
即 将主逻辑放在 try 中,而将异常处理逻辑放在 catch 中,
看上去非常整齐,对于阅读程序的人来说,也会非常理解你
的程序了
异常处理在 C++ 中的工作方式:
定义 3 个函数:f1()、f2()、f3(),并用 f2() 来调用 f1(),
用 f3() 来调用 f2()
如果 f1() 在运行过程中出现了异常,那么它就会把异常向上抛,
抛给它的调用者 f2(),如果 f2() 可以处理,则处理完成,如果
处理不了,就继续向上抛,抛给它的更上层
因为 f3() 调用了 f2(),所以,作为更上层的 f3() 就会捕获到异
常,捕获之后就会进行相应的处理,如果处理不了,就继续向
上抛,直到有函数可以处理
如果所有的函数都不能处理,就会抛到操作系统,操作系统就会
进行粗暴地干预了
看如下实例:
定义函数 fun1():
函数体中写出了 throw 1;,这里只是简单的抛出了一个数字 1,
实际的程序当然不能这么写,肯定要写很多正常的逻辑,当运行
到一个我们并不愿意看到的逻辑分支时,才通过 throw 抛出异常
在 main() 函数中:
通过 try … catch … 块来进行异常的捕获:
将 fun1() 的函数调用放在 try 块中,如果它能正常的运行完成,
那么就会执行 catch 块下面的代码,而 catch 块得不到执行
如果 fun1() 不能正常的完成自己的主逻辑,而在运行的过程中
不幸出现问题,抛出了 1,就必须要用 catch 来捕获它,捕获
之后,就可以在 catch 块中进行相应的处理
在 try 块中,如果运行到 fun1() 出现了异常,那么 fun1() 后面
的代码将不会得到运行
另外,注意:在 fun1() 中所抛出的是数字 1,它是 int 类型的,
在 catch 后的括号中写上 int 才能捕获,如果抛出的是 0.1,就
应该用 double 来进行捕获
对于 try … catch … 来说,它可以不是 一对一 的,可以是 一对多 的
对于一个 try 来说,里面有相应的主逻辑,主逻辑在运行过程中,
可能在第一行代码抛出异常,也可能在第三行代码抛出异常 …
抛出的异常可能是 int 类型的,也可能是 double 类型的,还有
可能是其它类型的
这时,就要根据不同的异常来做相应的处理,这种处理就非常的
细致了
如:接到 int 类型的异常,就要针对这种情况做相应的处理,告诉
用户怎么样了,用户就会根据你的提示帮助程序正常的往下运行
如果以上的所有的 catch 块,如:catch(int){}、catch(double){},
都不能捕获到相应异常,最后的 catch 块可以为大家兜底,注意写
法:catch(…){}
这种写法就是说:我可以捕获所有的异常,尽管来吧,所有的异常
我通通可以处理
但是,这种处理很野蛮,因为我们不分青红皂白,没有细致划分,
全部一刀切的在 catch(…){} 中写相应的处理代码,无非就是告诉
用户:你出错了,只能关闭
所以,不建议直接写一个 try 后马上跟一个 catch 加三个点,而是
在前面所有情况都处理不了,已经万般无奈了,才使用 catch(…){}
捕获异常,进行最后的挣扎
在上面的例子中有一个特点:所抛出的异常虽然是一个值,但捕获时,
只是一种数据类型,如果想要捕获这个值该怎么办呢?
看如下实例:
定义函数 getChar(),传入参数分别是字符串和下标,想要从字符串
中拿到对应下标的字符,但无法保证传入进来的下标比字符串长度短
如果下标比字符串的长度还长,就要通过 throw 将异常抛出,告诉
用户当前传入的下标是非法的
采用下面的方式拿到 throw 出来的字符:
在 catch 中,写的是 string& aval,即 取引用,此时如果传入字符串
hello world 和 下标 100,定然会抛出异常:invalid index!
这时就能通过 catch 拿到相应的值,并打印出来,清晰的告诉用户你
的下标传错了
注意:如果在 ch=getChar(str,100); 这一行代码就抛出了异常,那么
下一行代码 cout<<ch<<endl; 就得不到运行
在 C++ 中常见的异常:
异常处理与多态的关系
多态和异常处理有着非常紧密的联系,如下:
定义一个异常类:Exception,假设把它定义为一个接口类,在其中
定义一些打印的方法 或 异常处理的方法
然后通过细分的子类来继承接口类 Exception,当抛出子类对象时,
就都可以用这个父类去捕获了
看如下实例:
在 fun1() 中进行逻辑处理的过程中,不幸抛出了异常 SizeErr,
同理,在 fun2() 中进行逻辑处理时也抛出了异常 MemoryErr
二者都是 Exception 的子类,此时捕获异常的方法:
不论是 fun1() 还是 fun2(),都可以通过 try … catch … 块来捕获
异常,其中,关键是 catch 块中使用了父类 Exception,这时就可
以捕获到 fun1() 和 fun2() 中所抛出的子类对象,并通过子类对象
去调用相应的虚函数
程序:
Exception.h:
#ifndef EXCEPTION_H #define EXCEPTION_H
#include <iostream> using namespace std;
//接口类 Exception class Exception { public: virtual void printException()=0;
//其实,纯虚析构函数因为比较特殊,在接口类中, //也可以写为 virtual ~Exception(){} //这样就不用在 Exception.cpp 中单独为它实现了 virtual ~Exception() = 0; };
#endif |
Exception.cpp:
#include "Exception.h"
//因为将Exception类改为了接口类, //printException() 就不需要实现了 // //void Exception::printException() //{ // cout << "Exception--printException" << endl; //}
//注意: //接口类的纯虚析构函数比较特别,需要进行实现,即有函数体 //(接口类中普通的纯虚函数是没有函数体,不需要实现的) Exception::~Exception() {
} |
IndexException.h:
#ifndef INDEX_EXCEPTION_H #define INDEX_EXCEPTION_H #include "Exception.h"
class IndexException :public Exception { public: virtual void printException(); virtual ~IndexException(){} };
#endif |
IndexException.cpp:
#include "IndexException.h"
void IndexException::printException() { cout << "提示:下标越界!" << endl; } |
main.cpp:
#include <stdlib.h> #include "IndexException.h"
void test1(); void test2(); void test3(); void test4();
//抛出的异常只有被捕获到才能进行合理的处理 //或者说你有能力对其进行处理 // //如果抛出的异常捕获不到,等待计算机替你处理, //系统就会非常粗暴,你的程序就会崩溃掉 int main(void) { try { test1(); //换为 2 3 4 } //虽然不能实例化接口类的对象,但可以使用 //接口类的对象引用和对象指针 catch (Exception &e) { e.printException(); } catch (int) { cout << "exception--int" << endl; } catch (double &e) { cout << "exception--double:" << e << endl; } catch (...)//兜底用 catch(...){} { cout << "exception" << endl; } system("pause"); return 0; }
void test1() { throw IndexException(); }
void test2() { throw 10; }
void test3() { throw 0.1; }
void test4() { char ch = '#'; throw ch; } |
【made by siwuxie095】