C++面向对象入门(四十七)异常处理

异常处理(exception handling)机制是用于管理程序运行期间出现的非正常情况的一种结构化方法. C+++将异常处理的异常的检测和异常
处理分离, 增加了程序的可读性. 异常处理是提升程序健壮性的重要手段.
异常处理的基本思想
异常处理的概念: 程序的错误可以分为两种. 一种是编译错误, 即语法错误, 使用错误的语法, 函数, 结构和类, 程序就无法被生成运行
代码; 另一种是在运行时发生的错误, 运行时发生的错误又分为不可预料的逻辑错误和可以预料的运行异常.
逻辑错误: 程序设计不当造成的, 例如使用了不合适的排序算法, 导致在边界条件下不能正常完成排序任务. 一般只有用户做了某些出乎
意料的操作才会出现逻辑错误, 即使优秀的程序也不能避免.
解决逻辑错误的方法: 一旦发现了逻辑错误, 专门为其写一段处理错误的代码
运行异常: 可以预料, 但不可避免的由于昔日运行环境造成的错误, 如: 内存空间不足, 而程序运行中提出内存分配申请时得不到满足等.
解决运行异常的方法: 因为运行异常可以预料, 通常加入一些预防代码便可以防止这些异常.
异常处理的基本思想: 对于大型程序来说, 运行中一旦发生异常, 应该允许恢复和继续运行. 恢复的过程就是把产生异常所造成的恶劣影
响解决, 中间可能要涉及一系列的函数调用链的退栈, 对象的析构和资源的释放等. 继续运行是指处理完异常后, 在异常处理的代码区域
中继续运行.
C++中的异常: 从发生问题的代码区域传递到处理问题的代码区域的一个对象
基本思想:
1 实际的资源分配(如内存申请和文件打开)通常在程序的底层进行
2 当操作失败(如无法分配内存或打开一个文件)时, 在逻辑上通常是在程序的高层进行处理的, 中间还可能与用户交互.
3 异常为从操作失败的代码转向处理错误的代码提供了一种表达方式, 如果还存在中间层次的代码, 则为其释放资源提供了机会, 但并
不包括用于传递错误状态信息的代码.
异常处理的目的: 在异常发生时, 尽可能地减少破坏, 周密地善后, 而不影响其他部分程序的运行. 因此, 底层函数可以着重解决具体
问题, 不必过多考虑对异常的处理, 上层调用者可以在适当的位置设计对不同类型异常的处理.
抛出异常: 某个库软件或者自己书写的代码提供了一种机制, 能在出现异常情况时发出信号.
处理异常: 在程序的另一个地方, 需要添加合适代码来处理异常情况, 称为处理异常.
C++如何实现异常处理?
C++语言有一些处理异常的类, 并且实现异常处理的方法有多种, try, throw 和 catch语句就是C++语言用于实现异常处理的机制
C++处理异常的语法
1 try块语法: try块的定义指示可能在这段程序的执行过程中发生错误, 通常称为测试块, 其格式如下:
try
{
 <复合语句>
}
复合语句时代码的保护段. 如果预料某段程序代码(或对某个函数的调用)又可能发生异常, 就将它放在try语句块中. 如果这段代码(或
被调函数)运行时发生异常, 其中的throw表达式就会抛掷这个异常.
2 throw语法: 如果某段程序发生了自己不能处理的异常, 就可以使用throw表达式抛掷这个异常给调用者
throw <表达式>;
表达式表示异常类型, 可以是任意类型的一个对象, 包括类对象. 如果程序中有多处要处理异常, 应该用不同的操作数类型来互相区别,
操作数的值不能用来区别不同的异常.
throw语句类似于一个函数调用, 但并不是调用一个函数, 而是调用catch块. throw语句必须在try语句块内, 或者由try语句块中之间或
间接的调用函数来执行.
3 catch块语法:
由throw表达式抛掷的异常必须由紧跟其后的catch块捕获并处理.
catch(<异常类型1> 参数1)
{
 <处理异常1的复合语句>
}
catch(<异常类型2> 参数2)
{
 <处理异常2的复合语句>
}
.
.
.
catch(<异常类型n> 参数n)
{
 <处理异常n的复合语句>
}
catch(...)
{
 <处理任何异常的复合语句>
}
参数可以是某个类型的值, 也可以是引用. 这里的类型可以是任意有效的数据类型, 包括C++的类, 处理异常时不需要关心异常的参数值,
可以省略catch的参数名,. 在很多情况下只要通知处理异常程序由某个特定类型的异常已经产生就足够了. 但是在需要访问异常对象时
就要说明参数名, 否则无法访问catch子句的那个对象.
当异常被抛掷后, catch语句便依次被检查, 只有找到一个匹配的异常类型, 后面的异常处理catch语句都将被忽略. 如果异常类型声明是
一个省略号(...), catch子句便处理任何类型的异常, 因此这段处理程序必须是catch块的最后一段处理程序.
异常处理的执行过程:
1 语句执行到try块的包含段
2 如果在保护段执行期间没有引起异常, 那么跟随在try块后的catch子句就不执行, 程序从异常被抛掷的try块跟随的最后一个catch子句
后面的语句继续执行下去.
3 如果保护段执行期间有任何语句或调用的函数有异常被抛掷, 则通过throw操作创建一个异常对象(这隐含指可能包含一个拷贝构造函数).
. 对于这一点, 编译器在能够处理抛掷类型的异常的更高执行上下文中寻找一个catch子句或一个能处理任何异常类型的catch处理程序.
catch处理程序按其在try块后出现的顺序被检查. 如果没有找到合适的处理程序, 则检查外层的try块. 此处理持续到最外层的封闭try
块被检查完为止.
4 如果匹配的预测程序未找到, 则函数terminate()将被自动调用, 而函数terminate()的默认功能是调用abort()终止程序.
5 如果找到了一个匹配的catch处理程序, 则catch处理程序被执行, 接着程序跳转到所有catch块之后执行后续语句.
 
 
代码示例:
#include <iostream>
#include <fstream>
using namespace std;

/*
异常处理(exception handling)机制是用于管理程序运行期间出现的非正常情况的一种结构化方法. C+++将异常处理的异常的检测和异常
处理分离, 增加了程序的可读性. 异常处理是提升程序健壮性的重要手段.

异常处理的基本思想

异常处理的概念: 程序的错误可以分为两种. 一种是编译错误, 即语法错误, 使用错误的语法, 函数, 结构和类, 程序就无法被生成运行
代码; 另一种是在运行时发生的错误, 运行时发生的错误又分为不可预料的逻辑错误和可以预料的运行异常.

逻辑错误: 程序设计不当造成的, 例如使用了不合适的排序算法, 导致在边界条件下不能正常完成排序任务. 一般只有用户做了某些出乎
意料的操作才会出现逻辑错误, 即使优秀的程序也不能避免.

解决逻辑错误的方法: 一旦发现了逻辑错误, 专门为其写一段处理错误的代码

运行异常: 可以预料, 但不可避免的由于昔日运行环境造成的错误, 如: 内存空间不足, 而程序运行中提出内存分配申请时得不到满足等.

解决运行异常的方法: 因为运行异常可以预料, 通常加入一些预防代码便可以防止这些异常.

异常处理的基本思想: 对于大型程序来说, 运行中一旦发生异常, 应该允许恢复和继续运行. 恢复的过程就是把产生异常所造成的恶劣影
响解决, 中间可能要涉及一系列的函数调用链的退栈, 对象的析构和资源的释放等. 继续运行是指处理完异常后, 在异常处理的代码区域
中继续运行.

C++中的异常: 从发生问题的代码区域传递到处理问题的代码区域的一个对象

基本思想:
1 实际的资源分配(如内存申请和文件打开)通常在程序的底层进行
2 当操作失败(如无法分配内存或打开一个文件)时, 在逻辑上通常是在程序的高层进行处理的, 中间还可能与用户交互.
3 异常为从操作失败的代码转向处理错误的代码提供了一种表达方式, 如果还存在中间层次的代码, 则为其释放资源提供了机会, 但并
不包括用于传递错误状态信息的代码.

异常处理的目的: 在异常发生时, 尽可能地减少破坏, 周密地善后, 而不影响其他部分程序的运行. 因此, 底层函数可以着重解决具体
问题, 不必过多考虑对异常的处理, 上层调用者可以在适当的位置设计对不同类型异常的处理.

抛出异常: 某个库软件或者自己书写的代码提供了一种机制, 能在出现异常情况时发出信号.
处理异常: 在程序的另一个地方, 需要添加合适代码来处理异常情况, 称为处理异常.

C++如何实现异常处理?
C++语言有一些处理异常的类, 并且实现异常处理的方法有多种, try, throw 和 catch语句就是C++语言用于实现异常处理的机制

C++处理异常的语法
1 try块语法: try块的定义指示可能在这段程序的执行过程中发生错误, 通常称为测试块, 其格式如下:
try
{
    <复合语句>
}
复合语句时代码的保护段. 如果预料某段程序代码(或对某个函数的调用)又可能发生异常, 就将它放在try语句块中. 如果这段代码(或
被调函数)运行时发生异常, 其中的throw表达式就会抛掷这个异常.

2 throw语法: 如果某段程序发生了自己不能处理的异常, 就可以使用throw表达式抛掷这个异常给调用者
throw <表达式>;
表达式表示异常类型, 可以是任意类型的一个对象, 包括类对象. 如果程序中有多处要处理异常, 应该用不同的操作数类型来互相区别,
操作数的值不能用来区别不同的异常. 
throw语句类似于一个函数调用, 但并不是调用一个函数, 而是调用catch块. throw语句必须在try语句块内, 或者由try语句块中之间或
间接的调用函数来执行.

3 catch块语法:
由throw表达式抛掷的异常必须由紧跟其后的catch块捕获并处理.
catch(<异常类型1> 参数1)
{
    <处理异常1的复合语句>
}
catch(<异常类型2> 参数2)
{
    <处理异常2的复合语句>
}
.
.
.
catch(<异常类型n> 参数n)
{
    <处理异常n的复合语句>
}
catch(...)
{
    <处理任何异常的复合语句>
}

参数可以是某个类型的值, 也可以是引用. 这里的类型可以是任意有效的数据类型, 包括C++的类, 处理异常时不需要关心异常的参数值,
可以省略catch的参数名,. 在很多情况下只要通知处理异常程序由某个特定类型的异常已经产生就足够了. 但是在需要访问异常对象时
就要说明参数名, 否则无法访问catch子句的那个对象.
当异常被抛掷后, catch语句便依次被检查, 只有找到一个匹配的异常类型, 后面的异常处理catch语句都将被忽略. 如果异常类型声明是
一个省略号(...), catch子句便处理任何类型的异常, 因此这段处理程序必须是catch块的最后一段处理程序.

异常处理的执行过程:
1 语句执行到try块的包含段
2 如果在保护段执行期间没有引起异常, 那么跟随在try块后的catch子句就不执行, 程序从异常被抛掷的try块跟随的最后一个catch子句
后面的语句继续执行下去.
3 如果保护段执行期间有任何语句或调用的函数有异常被抛掷, 则通过throw操作创建一个异常对象(这隐含指可能包含一个拷贝构造函数).
. 对于这一点, 编译器在能够处理抛掷类型的异常的更高执行上下文中寻找一个catch子句或一个能处理任何异常类型的catch处理程序.
catch处理程序按其在try块后出现的顺序被检查. 如果没有找到合适的处理程序, 则检查外层的try块. 此处理持续到最外层的封闭try
块被检查完为止.
4 如果匹配的预测程序未找到, 则函数terminate()将被自动调用, 而函数terminate()的默认功能是调用abort()终止程序.
5 如果找到了一个匹配的catch处理程序, 则catch处理程序被执行, 接着程序跳转到所有catch块之后执行后续语句.
*/
//自定义除法函数
double dive(int x, int y);

/*
程序员子句处理异常
*/
void test1OfExceptionHandling();

/*
借助异常处理机制处理异常
*/
void test2OfExceptionHandling();

//自定义使用异常处理机制的除法函数
double diveWithExceptionHandling(int x, int y);

int main()
{
    //test1OfExceptionHandling();

    test2OfExceptionHandling();
    
    system("pause");
}

void test2OfExceptionHandling()
{
    ofstream log("log_no66.txt", ios::app);
    try
    {
        cout << "5 / 2 = " << dive(5, 2) << endl;
        cout << "8 / 0 = " << dive(8, 0) << endl;
        cout << "7 / 1 = " << dive(7, 1) << endl;
    }
    catch (int)
    {
        log << "Except of dividing zero" << endl;
        
    }
    cout << "End of program." << endl;
    log.close();
    return;
}

double dive(int x, int y)
{
    if (y == 0)
    {
        ofstream log("log_no66.txt", ios::app);
        log << "Except of dividing zero" << endl;
        log.close();
        exit(1);//退出程序
    }
    
    double dx = x, dy = y;
    return dx / dy;
}

void test1OfExceptionHandling()
{
    cout << "5 / 2 = " << diveWithExceptionHandling(5, 2) << endl;
    cout << "8 / 0 = " << diveWithExceptionHandling(8, 0) << endl;
    cout << "7 / 1 = " << diveWithExceptionHandling(7, 1) << endl;
}

double diveWithExceptionHandling(int x, int y)
{
    if (y == 0)
        throw y;
    return (double)x / (double)y;
}

 

posted @ 2020-09-09 12:18  DNoSay  阅读(320)  评论(0编辑  收藏  举报