C++中的错误处理机制:异常
C++中的错误处理机制:异常
在软件开发中,错误处理是确保程序稳定性和健壮性的关键环节。C++作为一种高级编程语言,提供了比C语言更为灵活和强大的错误处理机制——异常处理。异常处理机制允许程序在运行时检测到错误或异常情况时,能够以一种结构化和可预测的方式作出响应,从而提高代码的可读性、可维护性和异常安全性。
异常处理的基本概念
异常处理是一种编程范式,它通过抛出和捕获异常对象来处理运行时错误。在C++中,异常处理主要依赖于几个关键概念:异常抛出(Throw)、异常捕获(Catch)、异常传递(Exception Propagation)以及异常规范(Exception Specification,尽管自C++11起已不再推荐使用)。
异常抛出(Throw)
当程序执行过程中遇到错误或异常情况时,可以使用throw关键字来抛出一个异常对象。这个对象通常是派生自std::exception类的异常类对象,用于表示具体的错误状态。抛出的异常对象可以是任意类型的对象,但通常建议使用继承自std::exception的类,以便利用标准异常类的特性。
例如,自定义一个异常类MyException:
#include <iostream>
#include <exception>class MyException : public std::exception {
private:std::string message;public:MyException(const std::string& msg) : message(msg) {}const char* what() const noexcept override {return message.c_str();}
};void myFunction() {throw MyException("Something went wrong!");
}
异常捕获(Catch)
异常捕获通过try-catch语句块实现。try块用于包裹可能抛出异常的代码片段,而catch块则用于捕获并处理异常。可以根据需要在try块中添加多个catch块,以捕获并处理不同类型的异常。
try {myFunction();
} catch (const MyException& e) {std::cerr << "Caught MyException: " << e.what() << std::endl;
} catch (const std::exception& e) {std::cerr << "Caught std::exception: " << e.what() << std::endl;
} catch (...) {std::cerr << "Caught unknown exception" << std::endl;
}
在上面的例子中,try块中调用了myFunction,该函数可能抛出MyException类型的异常。第一个catch块尝试捕获MyException类型的异常,第二个catch块捕获所有继承自std::exception的异常(但非MyException),而最后一个catch(...)块则捕获所有类型的异常,作为一个兜底处理。
异常传递(Exception Propagation)
当异常在函数内部没有被捕获时,它会被传递给调用该函数的地方,并继续向上层函数传递,直到找到匹配的catch块或程序终止。这个过程称为异常传递或栈展开(Stack Unwinding)。
C++标准异常类
C++标准库提供了一系列标准异常类,用于表示各种常见的错误或异常情况。这些异常类都是从std::exception类继承而来的,提供了一种标准化的方式来处理异常情况。
std::logic_error:表示逻辑错误,即程序员编程错误导致的异常情况。std::invalid_argument:表示传递给函数的参数无效。std::length_error:表示容器超出了其最大允许长度。std::out_of_range:表示访问容器元素时超出了有效范围。std::runtime_error:表示运行时错误,通常是由于程序运行环境导致的异常情况。std::overflow_error:表示算术运算溢出。std::underflow_error:表示算术运算下溢出。std::range_error:表示数值超出了可表示的范围。std::bad_alloc:表示内存分配失败,通常是由于内存耗尽导致的异常情况。std::bad_cast:表示类型转换失败,通常是由于动态类型转换失败导致的异常情况。std::bad_typeid:表示类型标识符操作失败,通常是由于typeid运算符无法识别类型导致的异常情况。
异常处理的最佳实践
-
只在必要的情况下使用异常:异常处理是有开销的,因此在性能敏感的代码或经常执行的代码中,应避免过度使用异常。
-
使用具体的异常类:为不同类型的异常定义具体的异常类,并根据需要捕获和处理这些异常。这样可以提高异常处理的粒度,使代码更具可读性和可维护性。
-
在异常处理器中进行适当的清理和资源释放:在
catch块中,不仅要处理异常本身,还要确保进行必要的资源清理和释放工作,比如关闭文件句柄、释放内存等。这有助于防止资源泄露和其他潜在问题。 -
避免使用异常规范(自C++11起已弃用):C++早期版本中引入了异常规范,用于指定函数可能抛出的异常类型。然而,由于实践中这些规范往往被忽略或错误使用,且编译器很难进行有效检查,因此C++11起废除了异常规范的语法,并引入了
noexcept关键字作为更现代的替代方案。noexcept用于指明函数是否抛出异常,对于优化和异常安全性都有重要意义。 -
使用
noexcept:对于不会抛出异常的函数,应使用noexcept进行标记。这不仅可以提高程序的性能(因为编译器可以据此进行更高效的优化),还可以使异常处理逻辑更清晰。同时,如果noexcept函数确实抛出了异常,程序会立即调用std::terminate()终止,这有助于快速定位问题。 -
避免在析构函数中抛出异常:析构函数在对象生命周期结束时自动调用,用于释放资源。如果在析构函数中抛出异常,并且该异常在析构函数的调用过程中未被捕获,那么会导致程序调用
std::terminate()终止。因此,析构函数应该设计为不抛出异常,或者至少能够安全地处理自己抛出的异常。 -
考虑使用RAII(Resource Acquisition Is Initialization):RAII是一种在C++中管理资源(如动态内存、文件句柄、互斥锁等)的惯用法则。通过将对象的生命周期与资源的获取和释放绑定在一起,可以确保在对象被销毁时自动释放资源,从而避免资源泄露。RAII还可以与异常处理无缝结合,因为即使发生异常,对象的析构函数也会自动被调用,从而释放资源。
-
记录异常信息:在捕获异常时,除了立即处理异常外,还应考虑将异常信息记录下来,以便进行后续的问题分析和调试。这可以通过将异常信息写入日志文件、发送警报通知给相关人员或将异常信息显示在用户界面上等方式实现。
-
设计可测试的异常处理代码:为了确保异常处理代码的正确性和健壮性,应设计可测试的场景来验证异常处理逻辑。这包括模拟各种可能导致异常的情况,并验证异常是否被正确捕获和处理。
-
遵循异常安全保证:在编写涉及异常的代码时,应考虑代码的异常安全保证级别。通常有三种级别的异常安全保证:基本保证(不泄露资源,但状态可能不一致)、强保证(不泄露资源,且保持状态一致)和不抛出保证(不泄露资源,保持状态一致,且不抛出异常,这通常通过
noexcept实现)。在设计函数和类时,应明确其异常安全保证级别,并据此编写代码。
综上所述,C++中的异常处理机制是一种强大而灵活的错误处理手段。通过合理使用异常处理机制,并结合上述最佳实践,可以编写出更健壮、更易于维护的C++程序。
相关文章:
C++中的错误处理机制:异常
C中的错误处理机制:异常 在软件开发中,错误处理是确保程序稳定性和健壮性的关键环节。C作为一种高级编程语言,提供了比C语言更为灵活和强大的错误处理机制——异常处理。异常处理机制允许程序在运行时检测到错误或异常情况时,能够…...
概率论原理精解【9】
文章目录 集类拓扑空间基 参考文献 集类 C是一个集类(以G的某些子集为元素的集合称为G的集类)。 A i ∈ C , ∩ i 1 n A i ∈ C , 此为有限交封闭 C 所得集类 C ∩ f A_i \in C,\cap_{i1}^nA_i \in C,此为有限交封闭C所得集类C_{\cap f} Ai∈C,∩i1n…...
Pytorch添加自定义算子之(11)-C++应用程序将onnx模型编译并转成tensorrt可执行模型
一、环境 参考 opencv tensorrt cuda visual studio 2019 cmake 二、CMakeLists.txt配置文件 CMAKE_MINIMUM_REQUIRED(VERSION 2.8)PROJECT(segment)set(CMAKE_CXX_FLAGS "-std=c++14 -O1")link_directories(/usr/local/cuda/lib64) # set(OpenCV_DIR "/opt/…...
C++笔记1•C++入门基础•
1.C关键字 C总计63个关键字,C语言32个关键字: 2.命名空间: 在 C/C 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目…...
Linux查看系统线程数
Linux查看系统线程数 查看线程数查看进程内的线程统计线程数 查看线程数 想要查看Linux操作系统允许的最大线程数,可以通过命令 ulimit -a返回配置项的详细说明: # core文件的最大值为100blocks core file size (blocks, -c) 0# 进程的数…...
【Python基础】Python六种标准数据类型中哪些是可变数据,哪些是不可变数据
文章目录 1.基本介绍可变数据类型不可变数据类型2.可变和不可变到底指的是什么?可变(Mutable)不可变(Immutable)总结1.基本介绍 Python 中的六种标准数据类型分为可变数据类型和不可变数据类型。以下是这些数据类型的分类: 可变数据类型 列表(List) 列表是一种有序集…...
android13去掉安全模式 删除安全模式
总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题排查 3.修改方法 3.1方法1 3.2方法2 4.测试 5.彩蛋 1.前言 Android设备上的安全模式是一种诊断模式,当设备无法正常启动时,它会启动到这个模式。在这个模式下,只有系统自带的程序和服务会被运行,所有的第三方…...
LeetCode239 滑动窗口最大值
前言 题目: 239. 滑动窗口最大值 文档: 代码随想录——滑动窗口最大值 编程语言: C 解题状态: 没有思路,困难题,恐怖如斯 思路 本题的关键在于对单调队列的应用,时间复杂度 O ( n ) O(n) O(n)限…...
文件解析漏洞—IIS解析漏洞—IIS7.X
在IIS7.0和IIS7.5版本下也存在解析漏洞,在默认Fast-CGI开启状况下,在一个文件路径/xx.jpg后面加上/xx.php会将 “/xx.jpg/xx.php” 解析为 php 文件 利用条件 php.ini里的cgi.fix_pathinfo1 开启IIS7在Fast-CGI运行模式下 在 phpstudy2018 根目录创建…...
vue中子传父之间通信(this.$emit触发父组件方法和.sync修饰符与$emit(update:xxx))
文章目录 前言一、通过this.$emit触发父组件方法实现二、.sync修饰符与$emit(update:xxx) 前言 看了几篇帖子,都没有将$emit两种用法汇聚总结在一起。需要额外的浪费时间去查看其他帖子,索性自己总结一下,方便以后查看。 一、通过this.$emit…...
SocketIO 的 html 代码示例
话不多说,以下为前端 SocketIO 代码示例 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8"/><title>TestConnect</title><base><script src&quo…...
Vercel Error: (Azure) OpenAI API key not found
题意:Vercel 错误:(Azure) OpenAI API 密钥未找到 问题背景: I implemented openAI API in my Next.js app with the help of langchain library and it works superb on localhost, but in Vercel (ProVersion) it throws an error: 我使用…...
SPSS、Python员工满意度问卷调查激励保健理论研究:决策树、随机森林和AdaBoost|附代码数据
全文链接:https://tecdat.cn/?p37293 原文出处:拓端数据部落公众号 在深入了解公司当前的实际情况和员工内心真实想法的基础上,我们旨在从专业视角出发,为企业在组织管理方面的不足进行诊断,并进行全面审视。 为了…...
常见深度学习优化器总结
Adam 是一种非常流行的优化器,因为它结合了动量和自适应学习率的优点,通常在大多数情况下表现良好。不过,如果你在使用 Adam 时遇到性能问题或希望进一步调优,以下是几种可以考虑的替代优化器: 1. AdamW (Adam with W…...
python并发编程之多线程和多进程
了解过python 编程语言的人,可能很多人都听过,python的多线程是假的。 之所以这么说的原因是因为,大家应该都知道python 是一门解释型语言,在cpython解释器中有一个GIL(Global Interpreter Lock),全局解释锁ÿ…...
gorm入门——根据条件查询列表
在 GORM 中,你可以通过 Where 方法结合多个条件来查询符合条件的记录列表。Where 方法支持 AND 和 OR 条件,并且可以链式调用以构建复杂的查询。 示例代码: 假设你有一个 User 结构体,并希望根据多个条件查询符合条件的记录列表…...
笔面试编程题总结
8/6诺瓦星云 修改程序 void point(int *p){*p p[2];}; int main() {int c[] {1,2,3,4,5},*p c;point(p1);for(;p <c5;){printf("%d",*p);}return 0; }1、分隔字符串 strtok //c语言 #include <stdio.h> #include <string.h>// 函数声明 char* fin…...
[other][知识]八大行星的英文各是什么?
1、水星英文名 Mercury,水星最接近太阳,是太阳系中体积和质量最小的行星; 2、金星英文名 Venus,太阳系中第六大行星,太阳系中温度最高的行星,中国古代称之为太白或太白金星; 3、地球英文 E…...
如何使用 AWS CLI 创建和运行 EMR 集群
为初学者提供清晰易懂的教程 为初学者提供清晰易懂的教程 Apache Spark 和 AWS EMR 上的 Spark 集群 添加图片注释,不超过 140 字(可选) 欢迎来到雲闪世界。Spark 被认为是“大数据丛林之王”,在数据分析、机器学习、流媒体和图形…...
HDFS写入数据的流程图
1.客户端向namenode发送请求,请示写入数据 2.namenode接受请求后,判断这个用户是否有写入权限,如果不具备直接报错;如果有写入权限,接着判断在要写入的目录下是否已经存在这个文件,如果存在,直…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
