• 异常处理
  • Error
  • C Error
  • C和C++ 解决容错
  • 异常定义
    • 语法格式
    • 使用条例
    • 异常流程测试
  • 栈自旋
    • unwinding
    • RAII in Exception
  • Standard Exception
    • 异常自定义与抛出
    • 标准异常
      • 标准异常分类
      • 标准异常使用

异常处理

Error

Error 译为错误,常见的 Error 有编译时 Error 和运行时 Error。运行时的 Error 又分为不可预料的逻辑错误和可以预料的运行异常。

可以预料的运行异常,常见的有,I/0,文件打开失败,动态内存分配失败,越界访问等。

C Error

C 语言中错误的处理,通常采用返回值的方式或是置位全局变量的方式。这就存在两个问题。如果返回值正是我们需要的数据,且返回数据同出错数据 容错性不高。全局变量,在多线程中易引发竞争。而且,当错误发生时,上级函数要出错处理,层层上报,造成过多的出错处理代码,且传递的效率低下。

C++ 通过异常实现了返回与错误的处理的分离。

C和C++ 解决容错

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)       //逻辑代码area = sqrt(s * (s - x) * (s - y) * (s - z));else                                      //错误接口return -1.0;return area;
}
int main()
{int a, b, c;float area;while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);if (area == -1.0)cout << "输入的三角形不合法" << endl;elsecout << "Area:" << area << endl;}}return 0;
}

运行结果为:

上面函数triangleArea中把正常逻辑代码和错误码整合到一个函数接口进行返回。

那么C++里面对于错误就会单独处理。

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1.0;return area;
}
int main()
{int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "输入的三角形不合法" << endl;}return 0;
}

运行结果为:

上面代码中,如果出现一场,就抛出异常。不和功能代码进行整合。把错误处理和返回分离。

分析:

1,把可能发生异常的语句放在 try 语句声当中。try 不影响原有语句的执行流程。

2,若未发生异常,catch 子语句并不起作用。程序会流转到 catch 子句的后面执行。

3,若 try 块中发生异常,则通过 throw 抛出异常。throw 抛出异常后,程序立即离开本函数,转到上一级函数。所以 triangleArea 函数中的 return 语句不会执行。

图解说明:

4,throw 抛出数据,类型不限。既可以是基本数据类型,也可以是构造数据类型。

5,程序流转到 main 函数以后,try 语句块中抛出进行匹配。匹配成功,执行 catch语句,catch 语句执行完毕后。继续执行后面的语句。

6,如无匹配,系统调用 terminate 终止程序。

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1;return area;
}
int main()
{int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "输入的三角形不合法" << endl;}return 0;
}

上面代码异常返回值为1,但是catch参数类型为double 那么直接会出现问题:

运行结果:

我们可以换一种写法,catch参数为…

#include <iostream>
#include <cmath>
using namespace std;
double triangleArea(double x, double y, double z)
{double area;double s = (x + y + z) / 2;if (x + y > z && y + z > x && x + z > y)area = sqrt(s * (s - x) * (s - y) * (s - z));elsethrow - 1;return area;
}
int main()
{int a, b, c;float area;try {while (1){cin >> a >> b >> c;if (a > 0 && b > 0 && c > 0){area = triangleArea(a, b, c);cout << "Area:" << area << endl;}}}catch (double e){cout << "return " << e << endl;cout << "输入的三角形不合法" << endl;}catch (...){cout << "捕获到了未知异常" << endl;}return 0;
}

运行结果为:

如果没有任何捕获,系统将会杀死程序运行。

异常定义

语法格式

try
{被检查可能抛出异常的语句
}
catch(异常信息类型 [变量名])
{进行异常处理的语句
}

使用条例

1,被检语句必须放在 try 块中,否则不起作用。

2,try catch 中花括号不可省。

3,一个 try-catch 结构中,只能有一个 try 块,catch 块却可以有多个。以便与不同的类型信息匹配。

try{}
catch(double){}
catch(int){}
catch(char){}
catch(float){}

4,throw 抛出的类型,既可以是系统预定义的标准类型也可以是自定义类型。从抛出到 catch 是一次复制拷贝的过程。如果有自定义类型,要考虑自定义类型的拷贝问题。

5,异常匹配,不作类型转化。如果 catch 语句没有匹配异常类型信息,就可以用(…)表示可以捕获任何异常类型的信息。

catch(...)
{ cout<<"catch a  unknow exception"<<endl;
}

6.try-catch 结构可以与 throw 在同一个函数中,也可以不在同一个函数中,throw抛出异常后,先在本函数内寻找与之匹配的 catch 块,找不到与之匹配的就转到上一层,如果上一层也没有,则转到更上一层的 catch 块。如果最终找不到与之匹配的 catch 块,系统则会调有系统函数 terminate 使程序终止。

层级管理中,最好的处理方式就是在本层内解决,一旦出现异常层层上抛,工程量很大的时候就会打破层级管理的设计,所以不常使用。

异常流程测试

在中间环节,来测试异常的流程。

#include <iostream>using namespace std;//通过声明的方式,告知,调用方,如何处理
void func() throw(char)
{throw 'a';
}
//什么都没有写的情况-> 上抛
//写点什么,处理自己可以处理的部分 ,若无匹配上抛
void foo()
{try{func();}catch (int i){cout << "foo() catch " << i << endl;}catch (...) //若无匹配,写日志上抛{cout << "log throw up" << endl;throw;}
}
int main()
{try {foo();}catch (int i) {cout << "main() catch int " << i << endl;}catch (double i) {cout << "main() catch double " << i << endl;}return 0;
}

运行结果为:

如果抛出double类型,并且有double类型异常接受。

#include <iostream>using namespace std;//通过声明的方式,告知,调用方,如何处理
void func() throw(double)
{throw 1.1;
}
//什么都没有写的情况-> 上抛
//写点什么,处理自己可以处理的部分 ,若无匹配上抛
void foo()
{try{func();}catch (int i){cout << "foo() catch " << i << endl;}catch (...) //若无匹配,写日志上抛{cout << "log throw up" << endl;throw;}
}
int main()
{try {foo();}catch (int i) {cout << "main() catch int " << i << endl;}catch (double i) {cout << "main() catch double " << i << endl;}return 0;
}

运行结果为:

抛出异常声明
(1)一个不抛掷任何类型异常的函数可以声明为:
void func() throw(); //推荐使用

(2)如果在函数声明中没有包含异常接口声明,则函数可以抛掷任何类型的异常,
例如:
void func(); //不推荐

(3)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型。
例如:
void func() throw (A, B, C , D); //这个函数 func()能够且只能抛出类型 A B C D及其子类型的异常。

(4)如果一个函数抛出了它的异常接口声明所不允许抛出的异常,该函数默认行为调用 terminate 函数中止程序。

代码说明:

#include <iostream>
using namespace std;
class up {};
class down {};
void g()
{throw 1;
}
//异常规格说明,f 函数只能抛出 up 和 down 类型的异常
void f(int i)throw(up, down) {switch (i) {case 1: throw up();case 2: throw down();}g();
}
int main()
{try {// f(1);f(4);}catch (...){cout << "捕获一个未知异常" << endl;}return 0;
}

运行结果为:

栈自旋

unwinding

异常被抛出后,从进入 try 块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。

#include <iostream>
#include <memory>
using namespace std;
class A
{public:A(){cout << "A constructor" << endl;}~A(){cout << "~A destructor" << endl;}
};
int divide(int x, int y)
{A a;if (y == 0)throw('a');return x / y;
}
void myDivide(int x, int y)
{divide(x, y);
}
int main()
{try {myDivide(4, 0);}catch (int x) {cout << "x" << endl;cout << x << endl;}catch (double y) {cout << "y" << endl;cout << y << endl;}catch (...){cout << "no x, no y" << endl;}return 0;
}

运行结果为:

我们可以看到栈上发生了析构。

我们对于代码进行修改:

#include <iostream>
#include <memory>
using namespace std;
class A
{public:A(){cout << "A constructor" << endl;}~A(){cout << "~A destructor" << endl;}
};
int divide(int x, int y)
{A *p = new A;if (y == 0)throw('a');return x / y;
}
void myDivide(int x, int y)
{divide(x, y);
}
int main()
{try {myDivide(4, 0);}catch (int x) {cout << "x" << endl;cout << x << endl;}catch (double y) {cout << "y" << endl;cout << y << endl;}catch (...){cout << "no x, no y" << endl;}return 0;
}

运行结果为:

我们可以看到,如果是堆上的内存不会发生析构,那么就会导致内存泄露。
我们对于代码进行修改:

#include <iostream>
#include <memory>
using namespace std;
class A
{public:A(){cout << "A constructor" << endl;}~A(){cout << "~A destructor" << endl;}
};
int divide(int x, int y)
{A *p = new A;auto_ptr<A> pa(new A);  //RAII  资源获取(new)及初始化(构造)if (y == 0)throw('a');return x / y;
}
void myDivide(int x, int y)
{divide(x, y);
}
int main()
{try {myDivide(4, 0);}catch (int x) {cout << "x" << endl;cout << x << endl;}catch (double y) {cout << "y" << endl;cout << y << endl;}catch (...){cout << "no x, no y" << endl;}return 0;
}

运行结果为:

栈自旋也就是说,在抛出异常的时候要清栈,那么new的资源必须经过RAII包裹,否则就会泄露。

RAII in Exception

而堆上的空间,则会泄漏。利用遵循 RAII 思想的智能指针来解决。

Standard Exception

异常自定义与抛出

MyExcept

如果导致异常的因素有5个,我们不能抛出5次,所以就打包为一个类。

#include <iostream>using namespace std;class MyException
{public:MyException(){cout << "MyException constructor" << endl;}MyException(const MyException&){cout << "MyException copy constructor" << endl;}~MyException(){cout << "~MyException destructor" << endl;}
};
int divide(int x, int y)
{if (y == 0)throw(MyException());  //抛出类return x / y;
}
void myDivide(int x, int y)
{    divide(x, y);
}
int main()
{try {myDivide(4, 0);}catch (const MyException& a) {     //& cout << "catch self define myexception" << endl;}return 0;
}

运行结果为:

我们可以看到一次构造和一次析构。

标准异常

标准异常分类

标准异常使用

#include <iostream>
using namespace std;
int main()
{double* p;for (int i = 0; i < 1000; i++){p = new double[900000000];}return 0;
}

上面代码会编译会出现错误:

我们对于异常进行处理:

#include <iostream>
using namespace std;
int main()
{try {double* p;for (int i = 0; i < 1000; i++){p = new double[900000];}}catch (std::bad_alloc & e) {cout << e.what() << endl;exit(-1);}return 0;
}

运行结果为:

C++异常处理,Error,C和C++ 解决容错,栈自旋,Standard Exception【C++异常处理】(60)相关推荐

  1. mysql 5.7 1054_MySQL5.7更改密码时出现ERROR 1054 (42S22)的解决方法

    MySQL5.7更改密码时出现ERROR 1054 (42S22)的解决方法 发布时间:2020-10-14 16:01:38 来源:脚本之家 阅读:81 作者:剑侠365 新安装的MySQL5.7, ...

  2. vss error reading from file 解决方法

    vss error reading from file 解决方法 参考文章: (1)vss error reading from file 解决方法 (2)https://www.cnblogs.co ...

  3. Keil 5出现Error: L6218E: Undefined symbol解决方法

    Keil 5出现Error: L6218E: Undefined symbol解决方法 参考文章: (1)Keil 5出现Error: L6218E: Undefined symbol解决方法 (2) ...

  4. jquery Syntax error, unrecognized expression:的解决方法

    jquery Syntax error, unrecognized expression:的解决方法 参考文章: (1)jquery Syntax error, unrecognized expres ...

  5. 编译Qt“NMAKE:fatal error U1077”错误的解决方法

    编译Qt"NMAKE:fatal error U1077"错误的解决方法 分类: 错误与解决方法 2013-03-18 20:17 1775人阅读 评论(0) 收藏 举报 Qtfa ...

  6. mysql ERROR 1045 (28000): 错误解决办法

    本文分析了mysql登录报错提示:ERROR 1045 (28000)的解决方法.分享给大家供大家参考,具体如下: 一.问题: 公司linux系统的mysql数据库root用户设置过密码,但常常用命令 ...

  7. Mac cnpm装包时提示Error: EACCES: permission denied解决办法

    Cnpm装包时提示Error: EACCES: permission denied解决办法 2018年03月04日 09:31:51 miniminixu 阅读数:1598 版权声明:本文为博主原创文 ...

  8. mysql报错error2002_mysql中异常错误ERROR:2002的解决方法分享

    最近在启动mysql的时候发现mysql报错了,错误代码是2002,通过查找相关的资料发现是var/lib/mysql 的访问权限问题,所以这篇文章主要介绍了mysql中异常错误ERROR:2002的 ...

  9. yum时报Error: rpmdb open failed解决方法

    yum -y install telnet时报错: error: rpmdb: BDB0113 Thread/process 29682/140047880361792 failed: BDB1507 ...

最新文章

  1. 【每日一算法】填充同一层的兄弟节点
  2. 如何快速清空 Linux 中的大文件
  3. 用rollback()VS不用rollback()
  4. layui分页limit不显示_【图片】新手 分页显示不了呀【layui吧】_百度贴吧
  5. 微信公众帐号开发教程第14篇-自定义菜单的创建及菜单事件响应
  6. Linux学习总结(八)-磁盘格式化,挂载,swap扩容
  7. python爬取岗位数据并分析_Python年薪最高有50w|探秘全国近1600个Python岗位数据分析...
  8. splice删除选中列表_JS之删除数组中的元素的方法如delete和splice
  9. 在android中如何显示维语
  10. 基于HTML模板和JSON数据的JavaScript交互
  11. 基于turtle库用python画太极图案
  12. 关于计算机教室的寄语,教师寄语大全(精选90句)
  13. OTL/OCL/BTL/甲类/乙类/甲乙类
  14. windows优化大师怎么用_必读:我们赖以办公、游戏设计用的电脑该如何正确保养维护...
  15. mybatis 中between and用法
  16. python 透明图片合成_python:图片合成(PIL 库Image类模块)
  17. SCAU程序设计在线实训平台_实验_数据结构_实验4
  18. 身份证最后一位验证[python]
  19. c语言api获取百度地图,H5调用百度地图API获取地理位置
  20. 激活 window10 操作系统

热门文章

  1. 实验1 LINUX基本操作
  2. CTF-Python打包成的exe文件Re逆向
  3. cache/TLB里分别都有什么?
  4. python封装一个效率极高的 批量更新、插入合一的工具
  5. 【Echarts】 绘制世界地图和中国省份
  6. 2、ALTER TABLE:修改数据表
  7. Spring boot的Thymeleaf默认规则
  8. Nginx通过端口号区分虚拟机
  9. anemometer mysql_MySQL慢日志简介及Anemometer工具介绍 | | For DBA
  10. mysql2003错误如何解决_如何安装最新版redis6错误解决以及配置开机启动等