异常处理

----------------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】

原文地址:https://www.cnblogs.com/siwuxie095/p/6813146.html