【异常捕获】

news2024/9/28 23:35:34

异常捕获

  • 异常
    • 概念
    • 处理错误方式
  • 异常处理
    • 举例
    • 栈展开
    • 异常规范
    • 异常继承层次
    • 优缺点

异常

概念

异常时程序可能检测到的,运行时不正常的情况,如存储空间耗尽,数组越界等,可以预见可能发生在什么地方但不知道在什么时候发生的错误。
举例:

#include<vector>
using namespace std;
double Div(double a, double b)
{
return a/b;
}
int main()
{
double x,y,z;
cin>>x>>y; // 输入 1,2; 输入 2, 0 ;
z = Div(x,y);
cout<<"z: "<<z<<endl;
return 0;
}

例如上面两数相除的程序,分母为0便是错误。这就是异常。

处理错误方式

C语言中解决方法:

  • assert断言终止程序,断言不成立就会调用abort函数终止程序。但是该方法只在Debug版本有效,Release版本不会处理。
  • 返回错误码:很多库的接口函数都是通过把错误码放到errno中,表示错误信息。
  • 日志文件:将错误信息写入日志文件,出现致命错误终止程序。
    C++中异常机制:
    C++提供了一些内置的语言特性产生或者抛出异常,用来通知异常发生,然后预先按程序段来捕获catch,并且对他继续处理。
    举例:
class my_overflow_error { // 算术计算上溢
    const char* m_what;
public:
    my_overflow_error() :m_what(nullptr) {}
    my_overflow_error(const char* msg) :m_what(msg) {}
    ~my_overflow_error() { m_what = nullptr; }
    const char* what() const {
        return m_what != nullptr ? m_what : "unkonwn exception";
    }
};

double Div(double a, double b)
{
    if (0 == b)
    {
        throw my_overflow_error("除0错误");
    }
    return a / b;
}
int main()
{
    double x, y, z;
    cin >> x >> y; // 输入 1,2; 输入 2, 0 ;
    try
    {
        z = Div(x, y);
        cout << "z: " << z << endl;
    }
    catch (my_overflow_error& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}

编写步骤:

  1. throw表达式抛出异常。throw表达式也可以抛出任何类型的对象,如美剧,整除等。但最常用的是类对象。
  2. 使用try关键字,构成try语句块,该语句调用的函数能够抛出异常语句。
  3. 由catch字据捕获并处理异常。

异常处理

举例


template<class _Ty>
class PopOnEmpty
{
public:
    PopOnEmpty() = default;
    ~PopOnEmpty() = default;
};
template<class _Ty>
class PushOnFull
{
private:
    _Ty _value;
public:
    PushOnFull(const _Ty& x) :_value(x) {}
    ~PushOnFull() = default;
    const _Ty& Value() const { return _value; }
};
template<class _Ty>
class SeqStack
{
private:
    _Ty* _data;
    int _capa; // 容量
    int _top; // 栈顶指针
public:
    SeqStack(int sz = 10) :_capa(sz), _top(-1) {
        //_data = new _Ty[_capa];
        _data = (_Ty*)malloc(sizeof(_Ty)*_capa);
    }
    ~SeqStack() {
        free(_data);
        //delete[]_data;
    }
    int GetSize() const { return _top + 1; }
    bool IsEmpty() const { return GetSize() == 0; }
    bool IsFull() const { return GetSize() == _capa; }
    void Push(const _Ty& val)
    {
        if (IsFull())
        {
            throw PushOnFull<_Ty>(val);
        }
        //_data[++_top] = val;
         new(&_data[++_top]) _Ty(val);
    }
    const _Ty Pop()
    {
        if (IsEmpty())
        {
            throw PopOnEmpty<_Ty>();
        }
        return std::move(_data[_top--]);
    }
    const _Ty top() {
        if (IsEmpty()) {
            throw PopOnEmpty<_Ty>();
        }
        return _data[top];
    }
    void PrintStack() const
    {
        for (int i = 0; i <= _top; ++i)
        {
            cout << _data[i] << " ";
        }
        cout << endl;
    }
};
int main()
{
    const int n = 10;
    int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
    int br[n] = {};
    SeqStack<int> istack(8);
    try
    {
        for (int i = 0; i < n; ++i)
        {
            istack.Push(ar[i]);
            istack.PrintStack();
        }
    }
    catch (PushOnFull<int>& e)
    {
        cout << "栈满 ... 未入栈的数据是: " << e.Value() << endl;
    }
    try
    {
        for (int i = 0; i < n; ++i)
        {
            br[i] = istack.Pop();
        }
    }
    catch (PopOnEmpty<int>)
    {
        cout << "栈空" << endl;
        for (int i = 0; i < n; ++i)
        {
            cout << br[i] << " ";
        }
        cout << endl;
    }
    return 0;
}

观察上面代码,重点:

  • 在构造和析构栈的时候用了malloc和free而没有用new和delete来创建和释放栈,因为new在申请内存之后会直接进行创建对象,而malloc只申请内存,在初始化栈的时候只需要申请空间,不把对象存放进去。
  • 在push入栈的时候,会发现用了定位new而没有直接进行赋值,这里的原因是这样的。如果是内置类型这没有太大区别,但是如果是自定义的类对象,把对象给内存,如果不存在虚函数就不存在问题,但是如果存在虚表其赋值不会把虚函数也进行赋值,只有通过定位new重新对内存进行操作才可以把对象完整的移动到栈中。
  • 我们发现在Pop函数中我们都是以值类型返回而不是以对象进行返回,而且以值返回时用了move函数。原因是这样:如果以引用返回,举这样一个例子,如果栈中存在5个对象,出栈一个对象之后,但是以引用返回也就是说这个值在逻辑上不在栈中,但是物理上还在栈内,但在多线程的情况下另一个线程进行了入栈操作,那么就对我们返回的值进行了覆盖,返回的引用对象也就发生了改变。如果以值类型返回,也会出现一些问题,如果是自定义的类,出栈之后,会进行拷贝构造,而如果是拷贝构造,另一个线程进行入栈之后就会对原本的位置进行覆盖,原本内存的数据没有进行释放,导致内存泄漏。所以呢我们用了move函数,move函数移动了原本栈中的资源来移动构造了新对象,也不会出现内存泄漏。
  • 然后说一下异常捕获,在try区域出现异常之后,会直接跳过try中后面的代码在catch中进行处理该异常,不会处理完返回原本位置继续执行。

栈展开

因发生异常而逐步退出复合语句和函数的过程,被称为栈展开。
在这里插入图片描述
在throw中捕获到异常之后会通过异常类来创建一个异常对象来通过catch来解决这个异常,如果没有解决这个异常会跳出这一层,谁来调用这一部分,在上层来找catch来解决这个问题,如果在主函数中都没找到,那么会通过操作系统来终止程序处理。

异常规范

异常规范在于提供一种方案,可以列出想要抛出的异常。

template<class _Ty>
class SeqStack
{
//...
public:
void Push(const _Ty& val) throw(PushOnFull<_Ty>)
{
if (IsFull())
{
throw PushOnFull<_Ty>(val);
}
_data[++_top] = val;
}
const _Ty& Pop() throw(PopOnEmty<_Ty>)
{
if (IsEmpty())
{
throw PopOnEmpty<_Ty>();
}
return _data[_top--];
}
};

我们会发现在函数后加上了throw(类型名),该语句意为throw中参数的类型可以捕获,其他类型不需要捕获。虽然该方法已经C++11被舍弃了,但还需要了解一些。
在C++11中新增了noexcept关键字以表示这个函数不会抛出某种异常。并且可以阻止异常的传播。

void func() throw();//展开,还会找catch
void func() noexcept;//不展开,在当前函数中直接终止,不会catch
void func() noexcept(false);//假的不抛出异常
void func() noexcept(true);//真的不抛出异常

异常继承层次

在这里插入图片描述

上图中Excp继承出pushonFull和stackExcp类型,而(…)可以处理所有异常,所以呢要从pushonFul类型开始catch,如果直接用(…)处理其他catch就没意义了。

优缺点

使用C++异常机制缺点:

  • 增加开销,以来于处理器,操作系统,编译器等,程序性能明显下降,所以不建议使用异常。
  • 削弱编码人员安全意识。
  • 打乱程序正常执行流程,增加结构复杂度。
  • 资源释放不彻底,可能导致内存泄漏。
  • 降低代码复用率,使用了异常机制的代码不能直接给不适用异常机制的代码复用。

使用建议:
在C++语言本身抛出异常(new失败,STL),第三方库,系统库的接口抛出异常时,可以使用异常捕获机制。注意构造函数和析构函数不要抛出异常,因为对象构建一般抛出异常无法处理,释放资源时释放一半抛出异常难以处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/586734.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python中如何更新pip:一篇详细指南

Python中如何更新pip&#xff1a;一篇详细指南 作为一个有10年Python编程经验的工程师&#xff0c;我很清楚更新pip的重要性。pip是Python的依赖管理工具&#xff0c;它可以帮助您轻松安装、升级和删除Python包。随着Python不断发展和更新&#xff0c;保持最新版本的pip也很重…

SCI 投稿论文入门 —— 2. 图片编辑(Visio / Origin)

目录 引言IEEE trans论文图片格式要求单栏图片双栏图片 论文中插入曲线图曲线图具体要求 论文中插入结构图曲线图与结构图结合visio中设置界面单栏单张图片曲线图中需要插入结构图 箭头&#xff0c;线段粗细设置字体下标 引言 由于特殊要求&#xff0c;需要用word版本进行编辑…

Springboot整合Swagger2(3.0.0版本)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

SpringCloudAlibaba下篇(GateWay,Skywalking)(超级无敌认真好用,万字收藏篇!!!!)

文章目录 SpringCloudAlibaba下篇(GateWay,Skywalking)1 GateWay1.1 什么是网关1.2 GateWay介绍1.3 GataWay的基本使用1.4 GataWay整合Nacos1.5 断言路由工厂1.5.1 内置断言路由工厂1.5.2 自定义断言路由工厂 1.6 过滤器工厂1.6.1 内置局部过滤器工厂1.6.2 自定义局部过滤器1.6…

手撕code(2)

文章目录 1 设计模式1.1 单例模式1.1.1 懒汉单例1.1.2 饿汉单例1.1.3 总结 1.2 简单工厂模式 2 实现智能指针 1 设计模式 1.1 单例模式 某个类&#xff0c;不应该有多个实例&#xff0c;此时就可以使用单例模式。如果尝试创建多个实例&#xff0c;编译器就会报错。 1.1.1 懒…

【踩坑】mirai挂机运行经常自动退出怎么办?

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 背景介绍 解决思路 实现方法 最终效果 背景介绍 就是说&#xff0c;后台运行了mcl&#xff0c;但经常莫名其妙自动会退出&#xff0c;导致每次都得手动的去服务器上重新启动mcl。而对于自己运行的需要用…

“老年养生”APP的设计与开发

摘要&#xff1a;我国人口老龄化呈上升趋势&#xff0c;老年人口比重增加。这是我国经济发展的一大挑战&#xff0c;也是老年健康产业的一大机遇。随着我国经济发展&#xff0c;越来越多的人开始关注自己的身体&#xff0c;这导致各种关于健康的网络应用层出不穷。但是经过分析…

正则表达式与通配符 -- *?在正则表达式与通配符中的区别

1、前言 最近因为工作需要写一些自动化脚本&#xff0c;里面需要用到正则表达式来匹配特定的字符串&#xff0c;于是查了一些正则表达式相关的资料。资料里面都提到&#xff1a;*匹配前面的子表达式0次或任意多次。我当时就纳闷&#xff0c;*到底是表示的是匹配的次数还是可以…

2. JVM内存模型深度剖析与优化

JVM性能调优 1. JDK的体系结构2. Java语言的跨平台特性3.JVM整体结构及内存模型3.1 内存模型3.1.1 PC寄存器&#xff08;线程私有&#xff09;3.1.2 虚拟机栈&#xff08;线程私有&#xff09;1. 局部变量表2. 操作数栈 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确…

SimpleCG绘图函数(3)--绘制矩形

前面我们已经学习了点和线的绘制,本篇我们继续绘图函数学习----矩形的绘制&#xff0c;也就是长方形的绘制,并给出一个绘制楼房的例子演示矩形的应用。 所有绘制矩形相关函数如下&#xff1a; //以下矩形左上角坐标(left, top)&#xff0c;右下角坐标(right,bottom ) //以线条…

跨境电商系统开发-电商商城系统平台定制方案

随着业务的拓展&#xff0c;不少企业开始将目光转向国外市场&#xff0c;那么如何定制一套属于想自己的跨境出海电商商城方案呢&#xff1f;需要做好以下关口把关&#xff1a; 欢迎名片交流探讨开发平台流程 买家端(h5/pc/app) www.mardao.cn 账号 密码 卖家端(h5/pc)…

八、(重点)视图集ModelViewSet自定义action路由routers

上一章&#xff1a; 七、Django DRF框架GenericAPIView--搜索&排序&分页&返回值_做测试的喵酱的博客-CSDN博客 下一章&#xff1a; 九、DRF生成API文档_做测试的喵酱的博客-CSDN博客 一、视图集ModelViewSet与ReadOnlyViesSet ModelViewSet视图集 与 ReadOnly…

基于FPGA:运动目标检测(包围盒仿真工程,及一些调试问题)

目录 前言一、安装器件库二、仿真工程操作1、进入文件列表2、找到bounding_box_locate.vt&#xff0c;双击打开文件3、修改路径4、路径设置5、切换回“Hierarchy”&#xff0c;即工程界面6、运行仿真7、查看波形 重点&#xff1a;调试问题三、仿真代码1、仿真顶层文件2、绘制包…

node篇-fs模块儿

nodejs-fs模儿 异步 1. mkdir() 创建一个目录 // 1.mkdir 创建一个目录&#xff0c;回调函数的参数含义&#xff1a;err const fs require(fs); fs.mkdir(./avater,(err)>{console.log(err);if(err && err.code EEXIST){console.log(当前目录已经存在)} }) 当我…

华硕天选4R FA617原装Windows11原厂预装系统工厂模式恢复安装带 ASUSRecevory 一键还原22H2版本

华硕天选4R FA617X原装Windows11原厂预装系统工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1Pq09oDzmFI6hXVdf8Vqjqw?pwd3fs8 提取码:3fs8 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个16…

浅谈NoSQL数据库

数据库 数据库&#xff0c;又称为数据管理系统&#xff0c;是处理的数据按照一定的方式储存在一起&#xff0c;能够让多个用户共享、尽可能减小冗余度的数据集合&#xff0c;简而言之可视为电子化的文件柜——存储电子文件的处所。 数据库有&#xff1a;Oracle数据库、ACCESS数…

代码随想录算法训练营第四十五天 | 力扣 70. 爬楼梯(进阶), 322. 零钱兑换, 279.完全平方数

70. 爬楼梯&#xff08;进阶&#xff09; 题目 70. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 改为&#xff1a;一步一个台阶&#xff0c;两个台阶&#xff0c;三个台阶&#xff…

【浅学 JDBC】

浅学JDBC 笔记记录 一、1. JDBC的概念2. JDBC快速入门2.1 DriverManager2.2 Connection2.3 Statement2.4 ResultSet 3. JDBC入门案例使用3.1 查询所有学生信息3.2 根据id查询学生信息&&新增学生信息&&修改学生信息&&删除学生信息 一、 1. JDBC的概念 …

中科易安8周年,与你相约联网智能门锁

中科易安与物联网技术发展同频 持续推动安防信息化建设 打造多场景应用的数智化通行解决方案 促进技术与安全精准对接 联网智能门锁技术硬核 中科易安打造集NB-IoT、Sub-1G Cat.1、Wifi、RS485和BLE 5.0 在内的六大通信技术组网方案 以“联网”赋能智能门锁 实现通行数…

C++11之atomic原子操作

atomic介绍 多线程间是通过互斥锁与条件变量来保证共享数据的同步的&#xff0c;互斥锁主要是针对过程加锁来实现对共享资源的排他性访问。很多时候&#xff0c;对共享资源的访问主要是对某一数据结构的读写操作&#xff0c;如果数据结构本身就带有排他性访问的特性&#xff0c…