异常处理概述:

异常是用一个throw语句抛出,同时用try-catch来捕获,例如一个简单的例子:

#include <iostream>using namespace std;int main(int argc, char *argv[])
{cout << "Enter two number: " << endl;int number1, number2;cin >> number1 >> number2;try{if(number2==0)   // 如果除数为零 throw number1;   // throw语句抛出异常 cout << number1 << "/" << number2 << " is " << (number1/number2) << endl; }catch (int ex)   // catch捕获异常 ex: catch块参数{// 异常处理 cout << "Excetion: an number " << ex << "can not be divided by zero" << endl;}cout << "Exception end" << endl;return 0;
}

C++允许throw任何类型的值。当异常被抛出后,程序的正常执行流程被中断。当catch块捕获i到异常后,就执行里面的代码。

catch快块就像一个函数,其参数与抛出的异常值匹配,而与函数不同的是,catch块调用完毕后,程序控制流程不会返回到抛出异常的地方,而是直接执行catch块后的语句。

异常处理的优点:

将上述的代码改写为函数的形式:

#include <iostream>using namespace std;int quotient(int number1, int number2)
{if(number2==0)throw number1;return number1/number2;
}int main(int argc, char *argv[])
{cout << "Enter two number: " << endl;int number1, number2;cin >> number1 >> number2;try{int result = quotient(number1, number2);cout << number1 << "/" << number2 << " is " << result << endl; }catch (int)   // catch捕获异常 {// 异常处理 cout << "Excetion: an number can not be divided by zero" << endl;}cout << "Exception end" << endl;return 0;
}

quotient() 函数抛出异常,调用者的catch块会捕获到这个异常。这种机制允许一个函数给他的调用者抛出异常,否则函数自己必须处理这种异常,或者终止程序。例如错误发生时,一个被调用的函数,尤其是库函数,其自身不知道如何处理异常。库函数可以检测到错误,但只有函数调用者才知道如何处理异常。

异常处理的思路是将错误检测和异常处理分开。

异常类:

C++标准中的异常类

catch块的参数如果是类的话,则可以传递更多的信息

exception类定义在<exception>头文件中,类中包含一个虚函数what(),可以返回异常对象的错误信息

runtime_error是描述运行时错误的标准异常类的基类

overflow_error算术运算溢出

underflow_error溢出

logic_error描述逻辑错误

bad_alloc: new运算符在无法分配内存时抛出的异常

bad_cast是dynamic_cast在转换类型时发生错误所抛出的异常。

invalid_argument: 描述将非法的参数传递给函数时抛出的异常

out_of_range: 值超出允许范围

length_error: 对象大小超过最大允许长度

bad_except:  描述了从未预料的异常处理程序所抛出的异常

例如,对上面的代码进行修改,使用异常类。

#include <iostream>
#include <stdexcept>   // 包含异常类的头文件
using namespace std;int quotient(int number1, int number2)
{if(number2==0)throw runtime_error("Divisor can not be zero");  // 实例化一个runtime_error()对象 return number1/number2;
}int main(int argc, char *argv[])
{cout << "Enter two number: " << endl;int number1, number2;cin >> number1 >> number2;try{int result = quotient(number1, number2);cout << number1 << "/" << number2 << " is " << result << endl; }catch (runtime_error& ex)   // catch捕获异常, 参数为runtime_error对象 {// 异常处理 cout << ex.what() << endl;}cout << "Exception end" << endl;return 0;
}

可以在程序中同时捕获多个地方的异常:

#include <iostream>
#include <stdexcept>   // 包含异常类的头文件
using namespace std;int quotient(int number1, int number2)
{if(number2==0)throw runtime_error("Divisor can not be zero");  // 实例化一个runtime_error()对象 return number1/number2;
}double getArea(double radius)
{if(radius<0)throw invalid_argument("Radius can not be negative");return 3.14*radius*radius;
}int main(int argc, char *argv[])
{cout << "Enter two number: " << endl;int number1, number2;cin >> number1 >> number2;try{int result = quotient(number1, number2);cout << number1 << "/" << number2 << " is " << result << endl; }catch (runtime_error& ex)   // catch捕获异常, 参数为runtime_error对象 {// 异常处理 cout << ex.what() << endl;}double radius;cout << "Enter the radius: " << endl;cin >> radius;try{double area = getArea(radius); cout << "The area of the area is " << area << endl;}catch (invalid_argument& ex){cout << "Exception: " << ex.what() << endl; }cout << "Exception end" << endl;return 0;
}

自定义异常类:

C++允许定义自己的异常类。异常类与其他c++类没有什么差别,但是自定义的异常类应派生自exception类,这样就能够应用exception类中的一些公共特性(例如what()函数):

例如:定义一个派生自Geometric类的类Triangle, 在对Triangle的属性(边长)进行初始化和修改的时候,应该满足三角形三条边之间的关系,否则应该抛出异常,可以定义一个TriangleException来描述这个异常。

TriangleException.h文件      // 包含了类的实现,这种内联方式实现对于简短的函数来说效率更高

#ifndef TRIANGLEEXCEPTION_H
#define TRIANGLEEXCEPTION_H
#include <stdexcept>
using namespace std;// 自定义异常类 TriangleException
// TriangleException类派生自logic_error
class TriangleException: public logic_error
{private:     // 数据域 double side1;double side2;double side3;// 派生类中,如果没有显示的调用基类的构造函数, // 则在派生类的构造函数中会缺省调用基的无参构造函数, // 因为 logic_error类没有无参的构造函数, // 所以在这里需要显示调用基类有参数的构造函数。// 调用logic_error("Invalid triangle")设置了一个错误信息// 当异常对象调用what()时就会返回错误信息 public: TriangleException(double side1, double side2, double side3):logic_error("Invalid triangle") {this->side1 = side1;this->side2 = side2;this->side3 = side3;}double getSide1() const{return side1; }   double getSide2() const{return side2;}double getSide3() const{return side3;}
};
#endif

对triangle类的定义:

#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "E:\back_up\code\c_plus_code\chapter15\external_file\TriangleException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"     // 基类头文件
#include <cmath>
class Triangle: public Geometric
{private:double side1;double side2;double side3;bool isValid(double side1, double side2, double side3){return (side1<side2+side3)&&(side2<side1+side3)&&(side3<side2+side1);}public:Triangle(){side1 = 1;side2 = 2;side3 = 3;}Triangle(double side1, double side2, double side3){if (!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);   // 不满足三边关系则抛出异常 this->side1 = side1;this->side2 = side2;this->side3 = side3;}double getSide1() const{return side1;}double getSide2() const{return side2;}double getSide3() const{return side3;}void setSide1(double side1){if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);   // 不满足三边关系则抛出异常 this->side1 = side1;}void setSide2(double side2){if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);this->side2 = side2;}void setSide3(double side2){if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);this->side3 = side3;}double getPerimeter() const{return side1 + side2 + side3;}double getArea() const{double s = getPerimeter()/2;return sqrt(s*(s-side1)*(s-side2)*(s-side3));}
};
#endif 

main.cpp文件

#include <iostream>
#include <string>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\TriangleException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\rectangle.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\Triangle.h"using namespace std;int main(int argc, char *argv[])
{try{Triangle tria(3,3,4);tria.setSide1(1);cout << "The Area is " <<tria.getArea() << endl;//tria.setSide1(1); }catch (TriangleException& ex){cout << ex.what() << endl;cout << "The sides are " << ex.getSide1() << " " << ex.getSide2() << " " << ex.getSide3() << endl; }//displayGeometric(g1);//displayGeometric(circle1);    //  超类型的变量引用子类型的对象 //displayGeometric(rec1);//cout << "rec1 area is " << rec1.getArea() << endl;//cout << equalArea(circle1, rec1);//cout << "circle area is " << circle1.getArea() << endl;//cout << "The circle and rectangle area is equal? " << ((equalArea(circle1, rec1))?"Yes":"No") << endl;return 0;}

运行结果:// 捕获异常

多重异常捕获

一个try-catch模块可能包含多个catch语句。可以处理tr语句抛出的各种异常。

例如,对于前面三角形的例子,可以在定义一个异常类NoPositiveSideException(存在负的边长时也抛出异常)

NoPositiveSideException.h文件

#ifndef NOPOSITIVESIDEEXCEPTION_H
#define NOPOSITIVESIDEEXCEPTION_H
#include <stdexcept>using namespace std;
class NoPositiveSideException: public logic_error
{private:double side;//double side2;//double side3;public:NoPositiveSideException(double side): logic_error("No-Positive side!"){this->side = side;}double getSide() {return side;}
};
#endif

对Triangle类的修改:

#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "E:\back_up\code\c_plus_code\chapter15\external_file\TriangleException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\NoPositiveSideException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"     // 基类头文件
#include <cmath>
class Triangle: public Geometric
{private:double side1;double side2;double side3;bool isValid(double side1, double side2, double side3){return (side1<side2+side3)&&(side2<side1+side3)&&(side3<side2+side1);}public:Triangle(){side1 = 1;side2 = 2;side3 = 3;}Triangle(double side1, double side2, double side3){if(side1<=0)throw NoPositiveSideException(side1);if(side2<=0)throw NoPositiveSideException(side2);if(side3<=0)throw NoPositiveSideException(side3);if (!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);   // 不满足三边关系则抛出异常 this->side1 = side1;this->side2 = side2;this->side3 = side3;}double getSide1() const{return side1;}double getSide2() const{return side2;}double getSide3() const{return side3;}void setSide1(double side1){if(side1<=0)throw NoPositiveSideException(side1);if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);   // 不满足三边关系则抛出异常 this->side1 = side1;}void setSide2(double side2){if(side2<=0)throw NoPositiveSideException(side2);if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);this->side2 = side2;}void setSide3(double side2){if(side3<=0)throw NoPositiveSideException(side3);if(!isValid(side1, side2, side3))throw TriangleException(side1, side2, side3);this->side3 = side3;}double getPerimeter() const{return side1 + side2 + side3;}double getArea() const{double s = getPerimeter()/2;return sqrt(s*(s-side1)*(s-side2)*(s-side3));}
};
#endif 

main.cpp文件

#include <iostream>
#include <string>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\TriangleException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\NoPositiveSideException.h"   // 异常类头文件
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\rectangle.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\Triangle.h"using namespace std;int main(int argc, char *argv[])
{cout << "Enter the three sides: " << endl;double side1, side2, side3;cin >> side1 >> side2 >> side3;try{   Triangle tria(side1, side2, side3);//tria.setSide1(1);cout << "The Area is " <<tria.getArea() << endl;//tria.setSide1(1); }catch (TriangleException& ex)  //多重异常捕获{cout << ex.what() << endl;cout << "The sides are " << ex.getSide1() << " " << ex.getSide2() << " " << ex.getSide3() << endl; }catch (NoPositiveSideException& ex)   // 多重异常捕获{cout << ex.what() << endl;cout << "The side " << ex.getSide() << " is negative" << endl;}return 0;}

多个不同的异常类可以派生自同一个基类,如果catch的参数是基类的异常对象,则它能够捕获所有派生类的异常对象。还有catch模块的次序也很重要!派生类的catch在前,基类的catch在后

注:

catch的参数可以为(...),这同样的catch能捕获所有类型的异常。这种catch应放在所有的catch之后,作为默认异常处理程序,捕获所有没有被之前catch模块所捕获的异常。

异常的传播

在 try语句中发生异常的时候,c++会由前到后依次检查每个catch模块,检查异常对象是否与catch模块参数的类型相匹配。

重抛出异常:
一个异常被捕获后,他可以被重新抛出给函数的调用者。

#include <iostream>
#include <stdexcept>   // 包含异常类的头文件
using namespace std;void f1()
{try{throw runtime_error("Exception in f1"); }catch (exception& ex){cout << ex.what() << endl;cout << "Exception caught in f1" << endl;throw;    //  重抛出异常runtime_error() }
}int main(int argc, char *argv[])
{try{f1();   // f1内部抛出异常,处理后再重抛出异常 }catch (exception& ex)   // 捕获被重新抛出的异常{cout << "Exception caught in main" << endl;cout << ex.what() << endl;} return 0;
}

运行结果:

异常说明:

可以在函数的头部声明这个函数可能抛出的异常类型有哪些:

例如:

void f1() throw(runtime_error, logic_error)   // 异常说明,函数可能会抛出那些异常类,throw(ExceptionList)
{try{throw runtime_error("Exception in f1"); throw logic_error("Logic error");}
}

throw()称为空异常说明,放置于函数头后,说明函数不能抛出任何异常。

异常类型列表中如果有bad_exception,则函数抛出一列表中未定义的异常时,会抛出一个bad_exception异常,如果列表中没有bad_exception,发生这种情况时程序会终止。

------------------------------------------------------end---------------------------------------------------------

c++学习笔记(15) 异常处理相关推荐

  1. Hadoop学习笔记—15.HBase框架学习(基础知识篇)

    Hadoop学习笔记-15.HBase框架学习(基础知识篇) HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase ...

  2. C++语言学习笔记15:Clean 垃圾清理插件

    C++语言学习笔记15:Clean 垃圾清理插件 对话框 STET1 图片切换功能 导入位图资源 插入图片控件并修改属性 添加消息处理函数 step2 开发思路及类关系图 step3 添加控件及MFC ...

  3. 区块链学习笔记15——ETH状态树

    区块链学习笔记15--ETH状态树 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 引入 要实现的功 ...

  4. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  5. 点云学习笔记15——PCL常用的基础代码

    点云学习笔记15--PCL基础 命名规范 常用代码 1.时间计算 2.pcl::PointCloud::Ptr和pcl::PointCloud的两个类相互转换 3.如何查找点云的x,y,z的极值? 4 ...

  6. 凸优化学习笔记 15:梯度方法

    前面的章节基本上讲完了凸优化相关的理论部分,在对偶原理以及 KKT 条件那里我们已经体会到了理论之美!接下来我们就要进入求解算法的部分,这也是需要浓墨重彩的一部分,毕竟我们学习凸优化就是为了解决实际当 ...

  7. 影像组学视频学习笔记(15)-ROC曲线及其绘制、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(15)主要介绍: ROC曲线及其绘制 ROC 曲线 ROC = receiver operating characteristic cu ...

  8. 冰冰学习笔记:异常处理

    欢迎各位大佬光临本文章!!! 还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正. 本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位大佬.帅哥.美女点点支 ...

  9. cocos2d-x学习笔记15:cocos2d-x教程资源总结

    注:cocos2d可作为cocos2dx的参考,两者接口很相似. 名称:知易的<知易Cocos2D-iPhone开发教程> 官方地址:http://blog.sina.com.cn/s/a ...

最新文章

  1. 职场:5种幼稚表现,好多工作十年的人还在犯
  2. 帝国理工学院(IC)研究人员设计了一种可以解决瘫痪的脑机设备
  3. cglib与java反射的比较
  4. 筛选末位数字为1或5_看看广州示范性高中排行榜,怎么填报志愿?如何运用末位考生分数...
  5. linux grub2 修复,Ubuntu 10.04修复GRUB2
  6. 中等职业学校计算机专业定位,中等职业学校计算机专业教学探讨
  7. Flutter进阶—实现动画效果(七)
  8. Web 爬虫现已合法?
  9. sqlplus 设置显示格式
  10. AcWing 802. 区间和
  11. Python chapter 2amp;3 learning notes
  12. mysql关系代数表达式,【数据库复习】关系代数
  13. Spring的全局(统一)异常处理
  14. 王道考研操作系统复习笔记
  15. 基于Python(Django)+MongoDB实现的(Web)新闻采集和订阅系统【100010319】
  16. 如何完全的卸载ArcGIS?
  17. egg-views-ejs
  18. 去掉字符串头尾指定字符
  19. 新茶饮加速“去泡沫”
  20. 富文本编辑器 禁止改变文本内容

热门文章

  1. 微服务升级_SpringCloud Alibaba工作笔记0028---Nacos之Nacos集群配置下
  2. Netty工作笔记0010---Channel应用案例1
  3. SpringCloud工作笔记033---找不到import org.springframework.cloud.client.discovery.EnableDiscoveryClient;无法导入
  4. HTML5学习笔记---Html5简单理解,发展情况...
  5. 最近调试人脸问题的总结--命令行+抽取第二级子目录的名称
  6. 显示脸上的关键点的程序
  7. servlet的应用------request对象和bean实体的反射关系
  8. Entity Framework使用心得
  9. 简直要吐槽!!enable-migrations fails on x64 Projects
  10. shell读取mysql_shell读取mysql数据库