C++--第25课 - 异常处理 - 上

第25课 - 异常处理 - 上

1. 典型问题一

所有的代码都有可能不按照预定的方式进行

double Div(double a, double b)

{    return a / b;}

double Add(double a, double b)

{    return a + b;}

double Minus(double a, double b)

{    return a - b;}

double Multi(double a, double b)

{    return a * b;}

这一组数组单纯的实现了四则运算,但是出发函数却总是会有意外。很明显,当除数为0的首,Div函数的调用将产生无法预见的错误。

C方式的解决方案:

double Div(double a, double b, bool* valid)

{

*valid = true;

if( (-0.000000001 < b) && (b < 0.000000001) )

{

*valid = false;

return 0;

}

return a / b;

}

缺陷:

(1)每次调用Div函数后都必须判断结果是否合法。

(2)参数valid如果没有指向合法的内存空间会产生错误。(没有办法解决)

2. 典型问题二

程序中会出现大量的处理异常的代码

int MemSet(void* dest, unsigned int length, unsigned char v)  //将内存设置为某一个值

{

if( dest == NULL )

{

return INVALID_POINTER;

}

if( length < 4 )

{

return  INVALID_LENGTH;

}

if( (v < 0) || (v > 9) )

{

return  INVALID_PARAMETER;

}

unsigned char* p = (unsigned char*)dest;

for(int i=0; i<length; i++)

{

p[i] = v;

}

return SUCCESS;

}

C方式的典型调用代码

#include <cstdlib>

#include <iostream>

using namespace std;

int MemSet(void* dest, unsigned int length, unsigned char v)

{

if( dest == NULL )

{

return -1;

}

if( length < 4 )

{

return -2;

}

if( (v < 0) || (v > 9) )

{

return -3;

}

unsigned char* p = (unsigned char*)dest;

for(int i=0; i<length; i++)

{

p[i] = v;

}

return 0;

}

int main(int argc, char *argv[])

{

int ai[5];

double ad[4];

char ac[3];

int ret;

ret = MemSet(ai, sizeof(ai), 0);

if( ret == 0 )  //下面的代码没法一眼就看出来哪个是正确的,哪个是处理异常的

{

}

else if( ret == -1 )

{

}

else if( ret == -2 )

{

}

else if( ret == -3 )

{

}

ret = MemSet(ad, sizeof(ad), 1);

if( ret == 0 )

{

}

else if( ret == -1 )

{

}

else if( ret == -2 )

{

}

else if( ret == -3 )

{

}

ret = MemSet(ac, sizeof(ac), 2);

if( ret == 0 )

{

}

else if( ret == -1 )

{

}

else if( ret == -2 )

{

}

else if( ret == -3 )

{

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

缺陷:

正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以掩护。

其他C方式的异常处理方案(不常用,使得析构函数不被使用)

goto语句

setjmp()和longjmp()

#include <cstdlib>

#include <iostream>

using namespace std;

int MemSet(void* dest, unsigned int length, unsigned char v)

{

if( dest == NULL )

{

return -1;

}

if( length < 4 )

{

return -2;

}

if( (v < 0) || (v > 9) )

{

return -3;

}

unsigned char* p = (unsigned char*)dest;

for(int i=0; i<length; i++)

{

p[i] = v;

}

return 0;

}

int main(int argc, char *argv[])

{

int ai[5];

double ad[4];

char ac[3];

int ret = 0;

ret = MemSet(ai, sizeof(ai), 0);

if( ret != 0 )

{

goto ERROR;

}

ERROR:

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

在C语言中可以使用上面两种解决方案将异常处理代码放到统一的地方,与正常逻辑代码分开。

但在C++中,这两种方法可能导致对象的构造函数或者析构函数得不到调用而引发错误。

3. 典型问题三

C++中提供了try和catch语块对可能产生异常的代码进行分开处理:

try语块处理正常逻辑,catch语块处理异常。

C++语言中通过throw语句引发一个异常。

#include <cstdlib>

#include <iostream>

using namespace std;

#define DIV_ZERO_ERROR -1

double Div(double a, double b)

{

if( (-0.000000001 < b) && (b < 0.000000001) ) //抛出零异常

{

throw DIV_ZERO_ERROR;

}

return a / b;

}

int main(int argc, char *argv[])

{

try

{

cout<<Div(3, 1.1)<<endl;

cout<<Div(1, 0)<<endl;

cout<<Div(1, 2)<<endl;

//正常逻辑代码,可能产生除零异常

}

catch(int error)

{

cout<<"Exception: "<<endl; //处理除零异常

cout<<error<<endl;

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

2.72727

Exception:

-1

处理机制:

throw语句用于将异常“对象”抛出。

throw语句将异常抛出,如果在当前函数中没有try ... catch语句能够处理该异常,则当前函数将立即返回。异常被传递到上层调用函数,仍然需要try ... catch语句进行处理,如果上层函数也没有能力处理该异常,则异常继续向更上层函数的函数传递。

如果在函数调用栈中的所有函数都无法处理抛出的异常,则程序异常终止。

异常顺着函数调用栈“向上”传播,直到有响应的catch块处理。

4. 手把手写代码-使用try...catch

#include <cstdlib>

#include <iostream>

using namespace std;

void MemSet(void* dest, unsigned int length, unsigned char v)

{

if( dest == NULL )

{

throw -1;

}

if( length < 4 )

{

throw -2;

}

if( (v < 0) || (v > 9) )

{

throw -3;

}

unsigned char* p = (unsigned char*)dest;

for(int i=0; i<length; i++)

{

p[i] = v;

}

}

int main(int argc, char *argv[])

{

int ai[5];

double ad[4];

char ac[3];

try

{

MemSet(ai, sizeof(ai), 0);

MemSet(ad, sizeof(ad), 1);

MemSet(ac, sizeof(ac), 2);

}

catch(int e)

{

cout<<e<<endl;

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

-2

5. try ... catch匹配示例

同一个try语句块可以跟上多个catch语句块:同一个try语句块可以抛出多种不同类型的异常,不同类型的异常由不同的catch语句块负责处理。

异常被抛出后会自上而下注意匹配catch语句块:异常匹配时,不会进行默认类型转换。

#include <cstdlib>

#include <iostream>

using namespace std;

int test(int i)

{

if( i == 1 )

{

throw -1;

}

if( i == 2 )

{

throw "ERROR";

}

if( i == 3 )

{

throw 0.5;

}

if( i == 4 )

{

throw ‘d‘;

}

return i;

}

int main(int argc, char *argv[])

{

for(int i=0; i<3; i++)

{

try

{

cout<<test(i)<<endl;

}

catch(int e)

{

cout<<"Int: "<<e<<endl;

}

catch(const char* e)

{

cout<<"const char*: "<<e<<endl;

}

catch(double e)

{

cout<<"double: "<<e<<endl;

}

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

0

Int:-1

const char*:ERROR

若for(int i=0; i<3; i++)改成for(int i=0; i<5; i++),将有多余的项目是处理不了的,程序会运行不了。

小结:

异常处理是程序中随处可见的情况。

在工程实践中,大多数的代码都是用于处理异常的。

异常处理的质量直接决定最终产品的质量。

C++中提供了try ... catch语句用于将正常逻辑代码与异常处理代码进行分开处理。

原文地址:https://www.cnblogs.com/free-1122/p/11336308.html

时间: 2024-10-11 21:11:40

C++--第25课 - 异常处理 - 上的相关文章

C++--第26课 - 异常处理 - 下

第26课 - 异常处理 - 下 1. 问题一 有时在工程中关心是否产生了异常,而不关心具体的异常类型,C++语言中可以做到吗? C++中的catch语句可以使用...捕获所有的异常. #include <cstdlib> #include <iostream> using namespace std; int test(int i) { if( i == 1 ) { throw "p"; } if( i == 2 ) { throw 0.5; } if( i =

第三课 文件系统(上)

unix_c_03.txt====================第三课 文件系统(上)====================一.系统调用------------应用程序 -----------+| |v |各种库 |(C/C++标准库.Shell命令和脚本. |X11图形程序及库) || |v |系统调用 <----------+(内核提供给外界访问的接口函数,调用这些函数将使进程进入内核态)|v内核(驱动程序.系统功能程序)1. Unix/Linux大部分系统功能是通过系统调用实现的.如o

10.2: 现代软件工程这门课已经上了好几年了,以前有很多学生做过团队项目(说不定包括本校的学生),请你们找一个以前的团队采访一下-------------答题者:徐潇瑞

10.2: 现代软件工程这门课已经上了好几年了,以前有很多学生做过团队项目(说不定包括本校的学生),请你们找一个以前的团队采访一下 - 当时的项目有多少用户,给用户多少价值? 现在还有人用吗? - 这个项目能否给我们团队继续开发,源代码/文档还有么? - 项目开发有什么经验和教训 - 对学好软件工程有什么建议 写成一个博客   #团队博客作业2 根据老师的作业要求,我们采访了以前本科认识的一个同学,他在读本科的时候出去实习,参与了一些项目.他参与了手机外卖app的开发,根据他的回答,当时用户有1

第25课 《给外行讲前端三剑客》

今天是系列课程的第25课,我们今天来给外行介绍一下前端. 前端顾名思义就是比较靠前的那个工种,我们想象一下,我们一直向前.一直向前,最后会到哪里?一直往前就会最后到达用户那里.所以,前端就是最靠近用户的工种.有一个很形象的比喻:用户所能看到的一切都是前端.我们是最靠近用户的软件工程师,我们想让用户看见什么,用户就会看到什么.更进一步说,我们是保障用户体验的最后一道关 ,我们使用户感到:这个页面这么绚丽,这么页面这么舒适.别的方向的同行总会说,前端最轻松了,就写一些页面就可以了.但是我们就算别的不

JavaSE入门学习25:Java异常处理(上)

一异常简介 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的.比如说,你的代码少 了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用System.out.println(11/0),那么你是因为你用 0做了除数,会抛出java.lang.ArithmeticException的异常. Java异常是Java提供的用于处理程序中错误的一种机制. 异常发生的原因有很多,通常包含以下几大类: a用户输入了非法数据. b要打开的文件不存在. c

深入浅出CChart 每日一课——快乐高四第十三课 月上柳梢,Win32标准控件ChartCtrl之牵手

上节课笨笨介绍了新增加的ChartCtrl控件,是在对话框中使用的.本节课简单介绍这个控件的另一种用法. 首先按照以前的步骤建立一个Win32Application. 增加WM_CREATE消息的响应例程如下. case WM_CREATE: HWND hW; hW = CreateWindow(_T("ChartCtrl"), _T("Cap"), WS_CHILD | WS_VISIBLE, 20, 20, 600, 400, hWnd, NULL, hInst

SQL初级第三课(上)

先建立一个表 create table Student                 --学生(Sno          char(3) primary key ,  --学生学号Sname      char(8) not null,         --学生姓名Ssex        char(2) not null,          --学生性别Sbirthday datetime,                    --学生生日class        char(5)      

第25课 布局管理器(四)

1. 栈式布局管理器(QStackedLayout) (1)所有组件在垂直于屏幕的方向上被管理 (2)每次只有一个组件会显示在屏幕上(类似于窗口的Z-Order,但只能显示最顶层的) (3)只有最顶层的组件会被最终显示 2. 栈式布局管理器的特点 (1)组件大小一致且充满父组件的显示区 (2)不能直接嵌套其它布局管理器,但可以将一些组件放入一个layout,再将这个layout作为一个Widget的布局管理器.最后通过QStackedLayout.addWidget以达到嵌套的目的. (3)能够

Selenium第16课 文件上传下载

一.PyKeyboard方法 from pykeyboard import PyKeyboard from pymouse import PyMouse k = PyKeyboard() # k.press_key(k.enter_key) # k.release_key(k.enter_key) k.tap_key(k.enter_key)  # 下载文件时点击enter键 二.浏览器配置 浏览器设置下载时不弹窗:about:config --> browser.download.folder