【异常捕获】

异常捕获

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

异常

概念

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

#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),第三方库,系统库的接口抛出异常时,可以使用异常捕获机制。注意构造函数和析构函数不要抛出异常,因为对象构建一般抛出异常无法处理,释放资源时释放一半抛出异常难以处理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql倒库操作遇到的问题

背景&#xff1a;本地windows 10安装了mysql数据库后&#xff0c;需要把远程库的表结构和数据全部导入进来。 操作&#xff1a;导出数据库&#xff0c;导入数据库。 第一步&#xff1a;导出数据库 使用dump命令即可。 登陆mysql数据库 mysql -hhost --default-character-s…

海汽集团:业财共享服务中心建设推进集团数字治理

随着大数据时代的到来&#xff0c;数字化、信息化的财务管理方式应运而生。建立财务共享服务中心&#xff0c;走向业财一体化&#xff0c;已成为企业财务管理转型的必然趋势。 海汽集团作为全国唯一一家具有全省性客运网络的道路运输企业、海南道路运输业头部企业&#xff0c;…

3. 自然语言处理NLP:具体用途(近义词类比词;情感分类;机器翻译)

一、求近义词和类比词 1. 近义词 方法一&#xff1a;在嵌入模型后&#xff0c;可以根据两个词向量的余弦相似度表示词与词之间在语义上的相似度。 方法二&#xff1a;KNN&#xff08;K近邻&#xff09; 2. 类比词 使用预训练词向量求词与词之间的类比关系。eg&#xff1a;man&a…

​Lambda表达式详解​-初遇者-很细

目录 Lambda简介 对接口的要求 Lambda 基础语法 Lambda 语法简化 Lambda 表达式常用示例 lambda 表达式引用方法 构造方法的引用 lambda 表达式创建线程 遍历集合 删除集合中的某个元素 集合内元素的排序 Lambda 表达式中的闭包问题 Lambda简介 Lambda 表达式是 JD…

元宇宙应用领域-运动

元宇宙作为互联网的下一个阶段&#xff0c;目前已经发展成为一个多领域的“平行宇宙”&#xff0c;其中就包括体育。从体育的角度来看&#xff0c;元宇宙将是一个集运动、娱乐、社交、生活、学习于一体的“平行宇宙”&#xff0c;可以让人们在元宇宙中进行更好的运动&#xff0…

算法工程师的主要职责(合集)

算法工程师的主要职责 算法工程师的主要职责1 1、环境建模 根据设计的机器人方案&#xff0c;构建机器人的运动学模型、观测模型等概率学模型; 2、slam算法研发 研究基于多线激光雷达的slam算法&#xff0c;包括特征提取、数据关联、闭环检测等相关算法的开发; 3、定位算法研发…

MySQL-多表查询(中)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

力扣sql中等篇练习(二十七)

力扣sql中等篇练习(二十七) 1 连续两年有3个及以上订单的产品 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below WITH T as (SELECT t.product_id,t.d,count(order_id) numFROM(SELECT order_id,product_id,…

Axure教程—表格(中继器)

本文将教大家如何用AXURE中的中继器制作表格 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://oc3e6a.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87854863?spm1001.2014.3001.5501 二、功能介绍 可以在表格中插入…

Linux 系统大技能,搞定 90% 日常运维

一、Linux 系统日常运维九大技能 1、安装部署 方式&#xff1a;U盘&#xff0c;光盘和网络安装 其中网络安装已经成为了目前批量部署的首选方式&#xff1a;主要工具有Cobbler和PXEkickstart 可以参考如下链接内容&#xff1a; http://www.cnblogs.com/mchina/p/centos-px…

IP协议-服务类型字段

服务类型&#xff08;Type of Service&#xff09;字段是比较复杂的一个字段&#xff0c;该字段经过多次标准变更。 IPv4报文 一、最初标准&#xff08;RFC 791&#xff09; RFC 791定义TOS字段总共占用8bit&#xff0c;分为IP Precedence优先级&#xff08;3bit&#xff09;、…

JAVA商城源码-B2B2C商城系统-独立部署,一套源码终身可用

在现在电商迅速占领市场的时代里&#xff0c;选择开发商城系统已经成为了一种趋势&#xff0c;现在开发搭建商城系统有很多编程语言可以选择&#xff0c;目前在电商里市面上受到很多商家企业的喜爱的便是Java商城系统&#xff0c;那为什么要选择Java电商系统呢&#xff1f; 1、…

快递业的最新发展趋势:2023年市场预测

快递业是随着电子商务崛起而迅速发展的行业之一。自从互联网取代了线下商业模式&#xff0c;电子商务的发展成为了现代零售业的主要趋势&#xff0c;而快递业则变得越来越重要和不可或缺。未来的快递业需要应对许多挑战和机遇。 在2023年&#xff0c;快递业将进一步走向数字化、…

TatukGIS Developer Kernel 11.78 for .NETCore Crack

Tatuk GIS Developer Kernel for .NET 是一个变体&#xff0c;它是受控代码和 .NET GIS SDK&#xff0c;用于为用户 Windows 操作系统创建 GIS 专业软件的过程。它被认为是一个完全用于 Win Forms 的 .NET CIL&#xff0c;WPF 的框架是为 C# 以及 VB.NET、VC、oxygen 以及最终与…

ESP8266获取天气预报信息,并使用CJSON解析天气预报数据

一、实现功能 当前文章介绍如何使用ESP8266和STM32微控制器&#xff0c;搭配OLED显示屏&#xff0c;制作一个能够实时显示天气预报的智能设备。将使用心知天气API来获取天气数据&#xff0c;并使用MQTT协议将数据传递给STM32控制器&#xff0c;最终在OLED显示屏上显示。 心知…

第五十四天学习记录:C语言进阶:动态内存管理Ⅱ

常见的动态内存错误 1、对NULL指针的解引用操作 int* p(int*)malloc(4); //p进行相关的判断 *p10;//malloc开辟空间失败&#xff0c;有可能对NULL指针解引用 free(p); pNULL;2、对动态开辟的内存的越界访问 int* p(int*)malloc(40);//10个int if(p!NULL) {int i0;//越界for(…

Linux使用PowerShell模块管理MsSql-Server

1.安装PowserShell 更新包列表 sudo apt-get update 安装依赖: sudo apt-get install -y wget apt-transport-https software-properties-common 下载 key: wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb&…

chatgpt赋能python:Python内置函数:如何查找和使用?

Python内置函数&#xff1a;如何查找和使用&#xff1f; 作为一名有10年Python编程经验的工程师&#xff0c;我想与大家分享一下Python内置函数的使用技巧。Python内置函数是指已经定义好的函数&#xff0c;无需另外安装也无需导入就可以直接在Python中使用的函数。这篇文章将…

linuxOPS基础_linux文本文件查看

vi/vim vim文档编辑操作太多了,可以看这篇单独介绍vim的文章>https://blog.csdn.net/weixin_44368963/article/details/130963920 cat查看文件 命令&#xff1a;cat 作用&#xff1a;查看文件内容 语法&#xff1a;#cat 文件名称 ​ #cat 文件1 文件2 > 文件3 **特别注…

C++第六章:函数

函数 一、函数基础1.0 简介形参和实参形参列表函数的返回类型 1.1 局部对象自动对象局部静态对象 1.2 函数声明在头文件中进行函数声明 1.3 分离式编译编译和链接多个源文件 二、参数传递2.1 传值参数指针形参 2.2 传引用参数使用形参返回额外信息 2.3 const形参和实参指针或引…