【异常捕获】

news/2024/11/29 2:41:01/

异常捕获

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

异常

概念

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

#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.ppmy.cn/news/104974.html

相关文章

安卓Termux搭建web服务器【公网远程手机Android服务器】

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 转载自cpolar极点云的文章&#xff1a;【手机建站】TermuxCpolar内网穿透&#xff0c;搭建可以被外网访问的网站 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android…

vscode + CMake 构建C语言项目

文章目录 1. 所需工具2. 配置1. 编写顶级目录下的 CMakeLists.txt2. 编写子目录 src 里的 CMakeLists.txt3. 添加测试文件4. 开始构建 1. 所需工具 Visual Stduio Code&#xff08;vscode&#xff09; CMake 简介&#xff1a; CMake 是一个跨平台的 构建工具&#xff0c;用于 …

jdbc的入门

JDBC的基本介绍 JDBC即Java DataBase Connectivity&#xff08;Java数据库连接&#xff09;&#xff0c;是Java语言访问数据库的一种标准方法。JDBC提供了一组API&#xff0c;用于连接不同类型的数据库并执行SQL语句&#xff0c;以便与数据库进行交互。 JDBC API包括两部分&a…

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

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

算法基础学习笔记——⑬高斯消元\组合计数\容斥原理

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨高斯消元 ✨组合计数 &#x1f353;通过预处理逆元的方式求组合数: &#x1f353;Lucas定理: &#x1f353;分解质因数法求组合数&#xff1a; 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需…

SpringCloud(1)

文章目录 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&…

《深入理解计算机系统(CSAPP)》第7章 链接 - 学习笔记

写在前面的话&#xff1a;此系列文章为笔者学习CSAPP时的个人笔记&#xff0c;分享出来与大家学习交流&#xff0c;目录大体与《深入理解计算机系统》书本一致。因是初次预习时写的笔记&#xff0c;在复习回看时发现部分内容存在一些小问题&#xff0c;因时间紧张来不及再次整理…

【技术解决方案】(多级)缓存架构最佳实践

凌晨三点半了&#xff0c;太困了&#xff0c;还差一些&#xff0c;明天补上… 因为自己最近做的项目涉及到了缓存&#xff0c;所以水一篇缓存相关的文章&#xff0c;供大家作为参考&#xff0c;若发现文章有纰漏&#xff0c;希望大家多指正。 缓存涉及到的范围颇广&#xff0c…