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

下表是对上面层次结构中出现的每个异常的说明:
结合我们之前学过的面向对象,封装、继承、多态,我们可以发现,这些异常都是继承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那样,动不动就抛一个异常,以下是使用异常的一些标准
-
如果程序是逻辑错误,则不应该抛出异常,应该解决他
-
如果后面的代码,依赖这个结果,那么这个有异常情况,则可以抛出异常
-
如果后面的代码不依赖这个结果,则不应该抛出异常
-
异常不能用if……else代替
-
相关文章:
C/C++语言基础--C++异常看这一篇就够了
本专栏目的 更新C/C的基础语法,包括C的一些新特性 前言 通过前面几节课,我们学习了抽象、封装、继承、多态等相关的概念,接下来我们将讲解异常,异常是专门处理错误的;这一次加了不少图标,希望大家喜欢;C语…...
DFT ATPG中常见影响coverage的因素有哪些?
# DFT ATPG中常见影响Coverage的因素 ## 一、电路结构复杂性 1. **逻辑层次深度** - **原理** - 当电路的逻辑层次很深时,信号在传播过程中会经过多个逻辑门的处理。这使得测试向量难以准确地控制和观察内部节点的状态。例如,在一个具有多层嵌套逻辑的电路中,如一个…...
Python机器学习数据清洗到特征工程策略
Python机器学习数据清洗到特征工程策略 目录 ✨ 数据清洗:处理缺失值与异常值的策略🔄 特征选择:筛选与数据目标高度相关的特征🛠 特征工程:数据转换与生成新特征的多样化方法📊 类别型变量的数值化&…...
多线程-进阶(2)CountDownLatchConcurrentHashMapSemaphore
目的; JUC(java.util.concurrent) 的常⻅类 接着上一节课到 1.信号量 Semaphore 信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器。 理解信号量 可以把信号量想象成是停⻋场的展⽰牌: 当前有⻋位 100 个. 表⽰有 100 个可⽤资源. 当有⻋开进去的时候,…...
密码管理器KeePass的安装及使用
文章目录 软件下载安装汉化新建数据库创建\移动\修改 群组添加/修改/删除/移动 记录展示、搜索、锁定单独使用keepass生成密码的功能AES-256的密钥长度为256位,为啥可以设置超过32个字符的密钥? 软件下载 安装 分别解压:KeePass-2.53.1.zip&…...
星海智算:【萤火遛AI-Stable-Diffusion】无需部署一键启动
部署流程 1、注册算力云平台:星海智算 https://gpu.spacehpc.com/ 2、创建实例,镜像请依次点击:“镜像市场”->“更换”->“AI绘画”->“萤火遛AI-Stable Diffusion”。 程序首次启动可能需要几分钟,待实例显示“运行…...
JS生成器的特殊用法:委托yield*
yield 的基本用法 yield 用于在生成器函数中暂停函数执行,并返回一个值给外部调用者。当生成器再次被调用时,会从暂停的地方继续执行。 示例: function* simpleGenerator() {yield 1;yield 2;yield 3; }const gen simpleGenerator();cons…...
【CuPy报错】NVRTC_ERROR_COMPILATION (6)找不到 ‘vector_types.h‘
cupy安装不要再使用pip install cupy了, 已经替换成基于版本安装了pip install cupy-cuda12x,详见cupy官网。 安装完成后,在import cupy之后报错,找不到 ‘vector_types.h’: CompileException: /home/zoe/venv/lib/python3.10/…...
机器学习:知识蒸馏(Knowledge Distillation,KD)
知识蒸馏(Knowledge Distillation,KD)作为深度学习领域中的一种模型压缩技术,主要用于将大规模、复杂的神经网络模型(即教师模型)压缩为较小的、轻量化的模型(即学生模型)。在实际应…...
【C++入门篇 - 3】:从C到C++第二篇
文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意:在C语言中,如果内存不够…...
YOLOv8模型改进 第七讲 一种新颖的注意力机制 Outlook Attention
随着目标检测技术的不断发展,YOLOv8 作为最新一代的目标检测模型,已经在多个基准数据集上展现了其卓越的性能。然而,在复杂场景中,如何进一步提升模型的检测精度和鲁棒性依然是一个重要挑战。本文将探讨将 Outlook Attention 机制…...
C#多线程基本使用和探讨
线程是并发编程的基础概念之一。在现代应用程序中,我们通常需要执行多个任务并行处理,以提高性能。C# 提供了多种并发编程工具,如Thread、Task、异步编程和Parallel等。 Thread 类 Thread 类是最基本的线程实现方法。使用Thread类࿰…...
PHP DateTime基础用法
PHP DateTime 的用法详解 一、引言 在开发 PHP 应用程序时,处理日期和时间是一个至关重要的任务。PHP 提供了强大的日期和时间处理功能,其中 DateTime 类是最常用的工具之一。DateTime 类提供了丰富的方法来创建、格式化、计算和比较日期时间ÿ…...
一次Fegin CPU占用过高导致的事故
记录一下 一次应用事故分析、排查、处理 背景介绍 9号上午收到CPU告警,同时业务反馈依赖该服务的上游服务接口响应耗时太长 应用告警-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定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main…...
【React】使用 react hooks 需要遵守的原则
1)只能在顶层调用Hooks 这是指你不能在循环、条件语句或嵌套函数中调用Hooks。确保每次组件渲染时,Hooks的调用顺序保持一致。因此,你应该始终在React函数组件的最顶层调用Hooks。 React依赖于Hooks的调用顺序。如果这些调用在不同的渲染中顺…...
Python编程:创意爱心表白代码集
在寻找一种特别的方式来表达你的爱意吗?使用Python编程,你可以创造出独一无二的爱心图案,为你的表白增添一份特别的浪漫。这里为你精选了六种不同风格的爱心表白代码,让你的创意和情感通过代码展现出来。 话不多说,咱…...
腾讯IM SDK:TUIKit发送多张图片
一、问题描述 在使用腾讯IM DEMO(https://github.com/TencentCloud/chat-uikit-vue.git)时发现其只支持发送一张图片: 二、解决方案 // src\TUIKit\components\TUIChat\message-input-toolbar\image-upload\index.vue<inputref"inp…...
《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战
在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡(一台机器上只有一块GPU)还是单机多卡(一台机器上有多块GPU)的硬件配置上启动ChatGLM3-6B模型,其前置环境配置和项目文件是相同的。如果大家对配置过程还…...
Python 脚本来自动发送每日电子邮件报告
安装必要的库 我们将使用 smtplib 发送邮件,以及 email.mime 来创建电子邮件内容。另外,为了让脚本自动定时运行,可以使用操作系统的计划任务工具(如 Linux 的 cron 或 Windows 的 Task Scheduler)。 创建邮件内容 使…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
