当前位置: 首页 > news >正文

C/C++语言基础--C++异常看这一篇就够了

本专栏目的

  • 更新C/C++的基础语法,包括C++的一些新特性

前言

  • 通过前面几节课,我们学习了抽象、封装、继承、多态等相关的概念,接下来我们将讲解异常,异常是专门处理错误的;
  • 这一次加了不少图标,希望大家喜欢;
  • C语言后面也会继续更新知识点,如内联汇编;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

    • :eyes: 先看问题
    • :question: 什么是异常?
    • :arrow_upper_right: 抛出异常
    • :cat: 捕获异常
    • :star: C++ 标准的异常
    • :new: 定义新的异常(有头文件)
    • :accept: 万能接收异常
    • :stop_button: 抑制new抛异常
    • :see_no_evil: 函数中捕获异常注意点
    • :atom_symbol: 异常注意点:

👀 先看问题

我们先来看这一段代码:

void _div(double a, double b) 
{std::cout << a / b << std::endl;
}

这段代码很简单,就是实现一个除法运算,但是这个代码有一个很大的bug🍴🍴🍴,就是没有判断b!=0的情况,所以很多人写代码的时候是这样写的:

void _div(double a, double b) 
{if(b == 0) {std::cout << "除数不能为0" << std::endl;return;}std::cout << a / b << std::endl;
}

🚫 🚫🚫 ​ 但是如果我们忘记了写判断,而这一段代码在一个程序中无非就是一个小部分,如果我们忘记写这个判断了,程序就会引发中断,而这个是逻辑错误,不是代码错误,编译器是不会提示的,所以这个时候我们就需要一个一个去找bug的位置了🐛🐛🐛🐛🐛🐛,那有什么更好的解决方法呢?

当然有💪,这就是异常

❓ 什么是异常?

异常是程序在执行期间产生的问题,也就是指在程序运行时发生的特殊情况,比如我们上面的尝试除以零的操作。


为什么异常可以这样解决程序中产生的问题呢?

概念:异常处理提供了一种可以使程序执行的某点将控制流和信息转移到与执行先前经过的某点相关联的处理代码的方法,换言之,异常处理就是将控制权沿调用栈向上转移

🔑 关键:

  • 将程序运行的的PC指针沿着栈向上移动
  • 换句话说:就是将程序出现异常的时候那个位置移动到另外一个位置。

4️⃣ C++处理异常API

C++ 异常处理涉及到四个关键字:**try、catch、throw、noexcept **。

  • throw::程序会抛出一个异常。

  • catch: 通过异常处理程序捕获异常

  • try: try中存放是否需要检查有异常的的代码段,它后面通常跟着一个或多个 catch 块。

    • try
      {// 保护代码 
      }catch( ExceptionName e1 )  // ExceptionName 异常类型名字
      {// catch 块
      }catch( ExceptionName e2 )
      {// catch 块
      }catch( ExceptionName eN )
      {// catch 块
      }
      
  • **noexcept :**用于描述函数不会抛出异常,一旦有异常抛出,会立刻终止程序,它可以阻止异常的传播与扩散;

    • 扩展:noexcept可以带一个“常量表达式作为参数,常量表达式为true,表示不会抛出异常,否则代表可以抛出异常,如:noexpect(true) (也是默认的),noexpect(false)
    int show() noexpect  // 声明这个函数不会抛出异常
    {throw 0;
    }int show() noexpect(false)  // 声明这个函数*** 会 *** 抛出异常
    {throw 0;
    }
    

↗️ 抛出异常

我们上面可以知道,跑出异常的关键字是throw,那具体怎么使用呢?我们这里以解决我们上面的案例为例❗️❗️

double _div(double a, double b)
{if( b == 0 )  // 除数为0,错误,抛出异常{throw "Division by zero condition!";    }return (a / b);
}

🐱 捕获异常

catch 块跟在 try 块后面,用于捕获异常。我们可以指定想要捕捉的异常类型,通过指定 catch 关键字即可。

double _div(double a, double b)
{try{if (b == 0)  // 除数为0,错误,抛出异常{throw std::runtime_error("Division by zero condition!");}return (a / b);}catch (std::exception& err) {std::cout << err.what() << std::endl;}
}

上面的代码会捕获一个类型为 std::runtime_error的异常,catch存放处理异常方法,这样就不会发送中断了,运如图:

在这里插入图片描述


抛出异常代码模板结构大概如下:

try
{// 保护代码
}catch(...)
{// 能处理任何异常的代码
}

当然,catch还可以多个,多次匹配不同的问题:,如下代码🕶🕶🕶

double _div(double a, double b)
{try{if (b == 0)  // 除数为0,错误,抛出异常{throw 0;}return (a / b);}catch (const char* err) {std::cout << err << std::endl;}catch (int& err) {std::cout << 1 << std::endl;}
}

运行结果:

在这里插入图片描述

由于我们抛出了一个类型为 int 的异常,在捕获异常的时候,他会匹配不同抛出异常的类型,我们抛出的是整形

所以C++会自动寻找相对应位置。

如果throw的类型,在catch中没有找到会怎么样子呢?这个就留给读者思考吧🤔🤔🤔🤔

⭐️ C++ 标准的异常

C++ 提供了一系列标准的异常,定义在 中,这些异常是我们程序中容易犯错的结果,我们可以在程序中使用这些标准的异常,异常家族👪👪👪结构图如下(一部分):

在这里插入图片描述

下表是对上面层次结构中出现的每个异常的说明:

异常描述
std::exception该异常是所有标准 C++ 异常的父类
std::bad_alloc该异常可以通过 new 抛出。
std::bad_cast该异常可以通过 dynamic_cast 抛出。
std::bad_exception这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid该异常可以通过 typeid 抛出。
std::logic_error理论上可以通过读取代码来检测到的异常。
std::domain_error当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument当使用了无效的参数时,会抛出该异常。
std::length_error当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator
std::runtime_error用于表示那些在程序运行时可能发生的错误.
std::overflow_error当发生数学上溢时,会抛出该异常。
std::range_error当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error当发生数学下溢时,会抛出该异常。

结合我们之前学过的面向对象,封装、继承、多态,我们可以发现,这些异常都是继承exception类,所以,对于这些标准异常的捕获处理,我们就可以得到如下代码结构

try {throw 标准库中含有的异常;
}
catch(std::exception& err){} 

🆕 定义新的异常(有头文件)

您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:

#include <iostream>
#include <exception>
using namespace std;struct MyException : public exception			//exception 在std命名空间里面
{const char * what () const{return "C++ Exception";}
};int main()
{try{throw MyException();}catch(MyException& e){std::cout << "MyException caught" << std::endl;std::cout << e.what() << std::endl;}catch(std::exception& e){//其他的错误}
}

这将产生以下结果:

MyException caught
C++ Exception

🉑 万能接收异常

C++考虑的很全面,提供了一个能够捕获万能捕获异常的方法,...,代码结构如下:

try {throw "hello";
} catch (...) {}

我们将案例用这个方法捕获:

double _div(double a, double b)
{try{if (b == 0)  // 除数为0,错误,抛出异常{throw 0;}return (a / b);}catch (...) {std::cout << __FUNCTION__ << " 代码有问题" << std::endl;}
}

结果:

在这里插入图片描述

⏹ 抑制new抛异常

当使用new申请内存时,如果内存申请失败,会抛出std::bad_alloc异常,如果像让他不发生异常则需要如下处理(学习github某一位老师的笔记)。

  • 测试的时候,需要换成 x86 环境下(2g),x64 理论上无限内存
try
{while (true){new char[1024];}
}
catch (const std::bad_alloc& e)
{cout << "has exception "<<e.what() << endl;
}

如果想根据返回的指针来判断,就需要抑制new抛出异常。

double* p = nullptr;
do
{p = new(std::nothrow) double[1024];      //声明让 new 不抛出异常
} while (p);

🙈 函数中捕获异常注意点

当在函数中没有匹配处理该抛出异常的操作,这个时候会他会到函数调用的地方去寻找匹配,如下:

double _div(double a, double b)
{try{if (b == 0)  // 除数为0,错误,抛出异常,  *** 但是没有匹配 0 的代码 ***{throw 0;}return (a / b);}catch (const char* msg) {  // 匹配字符串类型的std::cout << __FUNCTION__ << " 代码有问题" << std::endl;}
}// 这个时候回到函数调用的地方找寻
int main()
{try {_div(10, 0);}catch (...) {  // 这个地方找std::cout << "main" << std::endl;}}

运行结果图如下:

在这里插入图片描述

⚛️ 异常注意点:

  • 类的构造函数不抛出异常

  • 异常不能乱用,C++一般用的不多,不像java那样,动不动就抛一个异常,以下是使用异常的一些标准

    1. 如果程序是逻辑错误,则不应该抛出异常,应该解决他

    2. 如果后面的代码,依赖这个结果,那么这个有异常情况,则可以抛出异常

    3. 如果后面的代码不依赖这个结果,则不应该抛出异常

    4. 异常不能用if……else代替

相关文章:

C/C++语言基础--C++异常看这一篇就够了

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 通过前面几节课&#xff0c;我们学习了抽象、封装、继承、多态等相关的概念&#xff0c;接下来我们将讲解异常&#xff0c;异常是专门处理错误的&#xff1b;这一次加了不少图标&#xff0c;希望大家喜欢;C语…...

DFT ATPG中常见影响coverage的因素有哪些?

# DFT ATPG中常见影响Coverage的因素 ## 一、电路结构复杂性 1. **逻辑层次深度** - **原理** - 当电路的逻辑层次很深时,信号在传播过程中会经过多个逻辑门的处理。这使得测试向量难以准确地控制和观察内部节点的状态。例如,在一个具有多层嵌套逻辑的电路中,如一个…...

Python机器学习数据清洗到特征工程策略

Python机器学习数据清洗到特征工程策略 目录 ✨ 数据清洗&#xff1a;处理缺失值与异常值的策略&#x1f504; 特征选择&#xff1a;筛选与数据目标高度相关的特征&#x1f6e0; 特征工程&#xff1a;数据转换与生成新特征的多样化方法&#x1f4ca; 类别型变量的数值化&…...

多线程-进阶(2)CountDownLatchConcurrentHashMapSemaphore

目的; JUC(java.util.concurrent) 的常⻅类 接着上一节课到 1.信号量 Semaphore 信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器。 理解信号量 可以把信号量想象成是停⻋场的展⽰牌: 当前有⻋位 100 个. 表⽰有 100 个可⽤资源. 当有⻋开进去的时候,…...

密码管理器KeePass的安装及使用

文章目录 软件下载安装汉化新建数据库创建\移动\修改 群组添加/修改/删除/移动 记录展示、搜索、锁定单独使用keepass生成密码的功能AES-256的密钥长度为256位&#xff0c;为啥可以设置超过32个字符的密钥&#xff1f; 软件下载 安装 分别解压&#xff1a;KeePass-2.53.1.zip&…...

星海智算:【萤火遛AI-Stable-Diffusion】无需部署一键启动

部署流程 1、注册算力云平台&#xff1a;星海智算 https://gpu.spacehpc.com/ 2、创建实例&#xff0c;镜像请依次点击&#xff1a;“镜像市场”->“更换”->“AI绘画”->“萤火遛AI-Stable Diffusion”。 程序首次启动可能需要几分钟&#xff0c;待实例显示“运行…...

JS生成器的特殊用法:委托yield*

yield 的基本用法 yield 用于在生成器函数中暂停函数执行&#xff0c;并返回一个值给外部调用者。当生成器再次被调用时&#xff0c;会从暂停的地方继续执行。 示例&#xff1a; function* simpleGenerator() {yield 1;yield 2;yield 3; }const gen simpleGenerator();cons…...

【CuPy报错】NVRTC_ERROR_COMPILATION (6)找不到 ‘vector_types.h‘

cupy安装不要再使用pip install cupy了&#xff0c; 已经替换成基于版本安装了pip install cupy-cuda12x&#xff0c;详见cupy官网。 安装完成后&#xff0c;在import cupy之后报错&#xff0c;找不到 ‘vector_types.h’: CompileException: /home/zoe/venv/lib/python3.10/…...

机器学习:知识蒸馏(Knowledge Distillation,KD)

知识蒸馏&#xff08;Knowledge Distillation&#xff0c;KD&#xff09;作为深度学习领域中的一种模型压缩技术&#xff0c;主要用于将大规模、复杂的神经网络模型&#xff08;即教师模型&#xff09;压缩为较小的、轻量化的模型&#xff08;即学生模型&#xff09;。在实际应…...

【C++入门篇 - 3】:从C到C++第二篇

文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意&#xff1a;在C语言中&#xff0c;如果内存不够…...

YOLOv8模型改进 第七讲 一种新颖的注意力机制 Outlook Attention

随着目标检测技术的不断发展&#xff0c;YOLOv8 作为最新一代的目标检测模型&#xff0c;已经在多个基准数据集上展现了其卓越的性能。然而&#xff0c;在复杂场景中&#xff0c;如何进一步提升模型的检测精度和鲁棒性依然是一个重要挑战。本文将探讨将 Outlook Attention 机制…...

C#多线程基本使用和探讨

线程是并发编程的基础概念之一。在现代应用程序中&#xff0c;我们通常需要执行多个任务并行处理&#xff0c;以提高性能。C# 提供了多种并发编程工具&#xff0c;如Thread、Task、异步编程和Parallel等。 Thread 类 Thread 类是最基本的线程实现方法。使用Thread类&#xff0…...

PHP DateTime基础用法

PHP DateTime 的用法详解 一、引言 在开发 PHP 应用程序时&#xff0c;处理日期和时间是一个至关重要的任务。PHP 提供了强大的日期和时间处理功能&#xff0c;其中 DateTime 类是最常用的工具之一。DateTime 类提供了丰富的方法来创建、格式化、计算和比较日期时间&#xff…...

一次Fegin CPU占用过高导致的事故

记录一下 一次应用事故分析、排查、处理 背景介绍 9号上午收到CPU告警&#xff0c;同时业务反馈依赖该服务的上游服务接口响应耗时太长 应用告警-CPU使用率 告警变更 【WARNING】项目XXX,集群qd-aliyun,分区bbbb-prod,应用customer,实例customer-6fb6448688-m47jz, POD实例CP…...

【Go初阶】两万字快速入门Go语言

初见golang语法 package mainimport "fmt"func main() {/* 简单的程序 万能的hello world */fmt.Println("Hello Go")} 第一行代码package main定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包&#xff0c;如&#xff1a;package main…...

【React】使用 react hooks 需要遵守的原则

1&#xff09;只能在顶层调用Hooks 这是指你不能在循环、条件语句或嵌套函数中调用Hooks。确保每次组件渲染时&#xff0c;Hooks的调用顺序保持一致。因此&#xff0c;你应该始终在React函数组件的最顶层调用Hooks。 React依赖于Hooks的调用顺序。如果这些调用在不同的渲染中顺…...

Python编程:创意爱心表白代码集

在寻找一种特别的方式来表达你的爱意吗&#xff1f;使用Python编程&#xff0c;你可以创造出独一无二的爱心图案&#xff0c;为你的表白增添一份特别的浪漫。这里为你精选了六种不同风格的爱心表白代码&#xff0c;让你的创意和情感通过代码展现出来。 话不多说&#xff0c;咱…...

腾讯IM SDK:TUIKit发送多张图片

一、问题描述 在使用腾讯IM DEMO&#xff08;https://github.com/TencentCloud/chat-uikit-vue.git&#xff09;时发现其只支持发送一张图片&#xff1a; 二、解决方案 // src\TUIKit\components\TUIChat\message-input-toolbar\image-upload\index.vue<inputref"inp…...

《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战

在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡&#xff08;一台机器上只有一块GPU&#xff09;还是单机多卡&#xff08;一台机器上有多块GPU&#xff09;的硬件配置上启动ChatGLM3-6B模型&#xff0c;其前置环境配置和项目文件是相同的。如果大家对配置过程还…...

Python 脚本来自动发送每日电子邮件报告

安装必要的库 我们将使用 smtplib 发送邮件&#xff0c;以及 email.mime 来创建电子邮件内容。另外&#xff0c;为了让脚本自动定时运行&#xff0c;可以使用操作系统的计划任务工具&#xff08;如 Linux 的 cron 或 Windows 的 Task Scheduler&#xff09;。 创建邮件内容 使…...

大语言模型与ChatGPT:深入探索与应用

文章目录 1. 前言2. 大语言模型的概述2.1 什么是大语言模型&#xff1f;2.2 Transformer架构的核心2.3 预训练与微调 3. ChatGPT的架构与技术背景3.1 GPT模型的演进3.2 ChatGPT的工作原理 4. ChatGPT的实际应用4.1 日常对话助手4.2 内容生成与写作4.3 编程辅助4.4 教育与学习辅…...

【从零开始的LeetCode-算法】3164.优质数对的总数 II

给你两个整数数组 nums1 和 nums2&#xff0c;长度分别为 n 和 m。同时给你一个正整数 k。 如果 nums1[i] 可以被 nums2[j] * k 整除&#xff0c;则称数对 (i, j) 为 优质数对&#xff08;0 < i < n - 1, 0 < j < m - 1&#xff09;。 返回 优质数对 的总数。 示…...

FastDFS VS MinIO:文件存储与对象存储的抉择(包含SpringBoot集成FastDFS范例)

FastDFS vs MinIO&#xff1a;文件存储与对象存储的抉择&#xff08;包含SpringBoot集成FastDFS范例&#xff09; 我坐在窗边&#xff0c;随着飞机穿过云层&#xff0c;在云层之上滑翔。可以清晰的看到飞机在天空留下的痕迹&#xff0c;不知道那是蔚蓝中的纯白&#xff0c;还是…...

【Redis】缓存预热、雪崩、击穿、穿透、过期删除策略、内存淘汰策略

Redis常见问题总结&#xff1a; Redis常见问题总结Redis缓存预热Redis缓存雪崩Redis缓存击穿Redis缓存穿透 Redis 中 key 的过期删除策略数据删除策略 Redis内存淘汰策略一、Redis对过期数据的处理&#xff08;一&#xff09;相关配置&#xff08;二&#xff09;内存淘汰流程&a…...

【LeetCode】每日一题 2024_10_15 三角形的最大高度(枚举、模拟)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;三角形的最大高度 代码与解题思路 久违的简单题 这道题读完题目其实不难想到有两条路可以走&#xff1a; 1、题目很明显只有两种情况&#xff0c;枚举是第一个球是红球还是蓝球这两种情…...

2024版最新网络安全工程师入门教程(非常详细)从零基础入门到精通,看完这一篇就够了

前言 想要成为网络安全工程师&#xff0c;却苦于没有方向&#xff0c;不知道从何学起的话&#xff0c;下面这篇 网络安全入门 教程可以帮你实现自己的网络安全工程师梦想&#xff0c;如果想学&#xff0c;可以继续看下去&#xff0c;文章有点长&#xff0c;希望你可以耐心看到…...

vue中关于router.beforeEach()的用法

router.beforeEach()是Vue.js中的路由守卫&#xff0c;用于在路由跳转前进行校验、取消、重定向等操作。 基本使用&#xff1a; const router new VueRouter({ ... })router.beforeEach((to, from, next) > {// ... }) to: 即将要进入的目标路由对象 from: 当前导航正要…...

C++模板初阶,只需稍微学习;直接起飞;泛型编程

&#x1f913;泛型编程 假设像以前交换两个函数需要&#xff0c;函数写很多个或者要重载很多个&#xff1b;那么有什么办法实现一个通用的函数呢&#xff1f; void Swap(int& x, int& y) {int tmp x;x y;y tmp; } void Swap(double& x, double& y) {doubl…...

【数据结构 | 红黑树】红黑树的性质和插入结点时的调整

文章目录 红黑树红黑树插入时的调整&#xff1f;1. 插入结点是根结点2. 插入结点的叔叔是红色3. 插入结点的叔叔是黑色LL 型RR型LR型RL型 红黑树 前提&#xff1a;二叉搜索树&#xff08;左 < 根 < 右&#xff09;—— 左根右根和**叶子&#xff08;NULL&#xff09;**都…...

mysql学习教程,从入门到精通,SQL导入数据(44)

1.SQL 导出数据 以下是一个关于如何使用 SQL 导出数据的示例。这个示例将涵盖从一个关系数据库管理系统&#xff08;如 MySQL&#xff09;中导出数据到 CSV 文件的基本步骤。 1.1、前提条件 你已经安装并配置好了 MySQL 数据库。你有访问数据库的权限。你知道要导出的表名。…...