C++----异常
一、C 语言传统的错误处理方式
在 C 语言中,处理错误主要有两种传统方式,每种方式都有其特点和局限性。
1. 终止程序
- 原理:使用类似
assert这样的断言机制,当程序运行到某个条件不满足时,直接终止程序的执行。 - 示例代码
#include <assert.h> #include <stdio.h>void divide(int a, int b) {assert(b != 0); // 断言b不为0,如果b为0,程序会终止printf("%d / %d = %d\n", a, b, a / b); }int main() {divide(10, 0);return 0; } - 缺陷:这种方式对用户不太友好,因为一旦出现错误,程序会直接崩溃,用户无法进行一些必要的处理或恢复操作。例如在发生内存错误、除 0 错误等情况时,程序会突然终止,给用户带来不好的体验。
2. 返回错误码
- 原理:函数在执行过程中,如果遇到错误,会返回一个特定的错误码。程序员需要根据这个错误码去查找对应的错误信息。在系统的很多库的接口函数中,会把错误码放到
errno变量中,表示发生的错误。 - 示例代码
#include <stdio.h> #include <errno.h> #include <string.h>int divide(int a, int b) {if (b == 0) {errno = EINVAL; // 设置错误码为无效参数return -1;}return a / b; }int main() {int result = divide(10, 0);if (result == -1) {printf("Error: %s\n", strerror(errno)); // 根据错误码输出错误信息} else {printf("Result: %d\n", result);}return 0; } - 缺陷:需要程序员手动去查找和处理错误码对应的错误信息,增加了开发的复杂度。而且不同的库可能使用不同的错误码体系,容易造成混淆。
实际应用情况
在实际的 C 语言编程中,基本都是使用返回错误码的方式处理错误,只有在处理非常严重的错误时,才会使用终止程序的方式。
二、C++ 异常概念
C++ 引入了异常机制,为错误处理提供了一种更加灵活和强大的方式。当一个函数发现自己无法处理的错误时,可以抛出异常,让函数的直接或间接调用者来处理这个错误。
1. 异常处理的关键字
throw:当问题出现时,程序会抛出一个异常。通过使用throw关键字来完成,后面可以跟任意类型的数据,如整数、字符串、自定义对象等。catch:用于捕获异常。可以有多个catch块,每个catch块可以捕获不同类型的异常。try:try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块。try块中的代码被称为保护代码。
2. try/catch语句的语法
try
{// 保护的标识代码
}
catch( ExceptionName e1 )
{// catch 块,处理类型为ExceptionName的异常
}
catch( ExceptionName e2 )
{// catch 块,处理类型为ExceptionName的异常
}
catch( ExceptionName eN )
{// catch 块,处理类型为ExceptionName的异常
}
3. 示例代码
#include <iostream>
using namespace std;// 定义一个除法函数,可能会抛出异常
double Division(int a, int b) {// 当b == 0时抛出异常if (b == 0) {throw "Division by zero condition!";} else {return ((double)a / (double)b);}
}// 定义一个函数,调用Division函数并处理异常
void Func() {try {int len, time;cout << "请输入两个整数(用空格分隔): ";cin >> len >> time;cout << "除法结果: " << Division(len, time) << endl;}catch (const char* errmsg) {cout << "捕获到异常: " << errmsg << endl;}}int main() {try {Func();}catch (const char* errmsg) {cout << "捕获到字符串类型异常: " << errmsg << endl;}return 0;
}
三、异常处理基础
1. 异常抛出与捕获原则
-
抛出机制:通过抛出对象触发异常(可抛出任意类型对象)
-
匹配规则:
-
匹配类型相同且位置最近的catch块
#include <iostream>// 函数B抛出整数类型异常 void functionB() {throw 42; }// 函数A调用函数B,并尝试捕获异常 // functionB 抛出一个 int 类型异常。 void functionA() {try {functionB();} catch (double) {std::cout << "在functionA中捕获到double类型异常" << std::endl;} catch (int num) {//与第二个 catch 块匹配,且它离异常抛出点 functionB 最近,//所以会执行该 catch 块,输出“在functionA中捕获到int类型异常,//值为: 42”。std::cout << "在functionA中捕获到int类型异常,值为: " << num << std::endl;} }int main() {try {functionA();} catch (...) {//如果functionA中没有匹配int类型的catch块,异常会传递到main函数,//main函数中的 catch(...) 会捕获所有类型异常。std::cout << "在main中捕获到其他类型异常" << std::endl;}return 0; } -
会生成异常对象的拷贝(保证异常对象有效性)
// 抛出字符串异常示例 double Division(int a, int b) {if (b == 0) {string s("Division by zero!");throw s; // 抛出拷贝后的临时对象}return static_cast<double>(a)/b; }void func() {int x, y;cin >> x >> y;cout << Division(x, y) << endl; }int main() {while (true) {try {func();}catch (const string& err) { // 捕获引用避免拷贝cout << "Error: " << err << endl;}} } -
派生类异常可用基类捕获(实际开发常用方式)
-
2. 通用捕获与继承体系
// 通用捕获与继承示例
class BaseException {};
class MathException : public BaseException {};int main() {try {throw MathException();}catch (const BaseException&) { // 基类捕获派生类异常cout << "Base exception caught" << endl; }catch (...) { // 最后防线捕获所有异常cout << "Unknown exception" << endl;}
}
四、异常传播机制
1. 栈展开过程
-
检查throw所在try块
-
逐层回退调用栈查找匹配catch
-
到达main未匹配则程序终止
-
异常处理后继续执行catch块后续代码

2. 异常重新抛出
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再// 重新抛出去。int* array = new int[10];try {int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;}// ...cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
} 五、异常安全规范
1. 关键原则
-
构造函数:避免抛出异常(可能导致对象不完整)
-
析构函数:禁止抛出异常(防止资源泄漏)
-
RAII机制:通过智能指针等实现资源自动管理
2.异常规范
- 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的 后面接throw(类型),列出这个函数可能抛掷的所有异常类型。
- 函数的后面接throw(),表示函数不抛异常。
- 若无异常接口声明,则此函数可以抛掷任何类型的异常。
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept; 六、自定义异常体系设计
1. 服务器开发典型继承体系
在服务器开发中,为了更好地管理和处理不同类型的异常,通常会设计一个异常继承体系。以下是一个示例代码:
// 异常基类(抽象错误类型)
// Exception 类:作为基类异常,包含异常信息 _errmsg 和异常编号 _id,
// 并定义了虚函数 what() 用于返回异常信息,支持多态。
class Exception {
public:Exception(const string& errmsg, int id): _errmsg(errmsg), _id(id) {}virtual string what() const {return "[" + to_string(_id) + "] " + _errmsg;}virtual ~Exception() = default; // 虚析构保证正确释放protected:string _errmsg; // 错误描述int _id; // 错误编号
};// SQL操作异常(具体错误类型)
// SqlException 类:继承自 Exception 类,添加了 _sql 成员变量,重写了
// what() 函数,返回更详细的 SQL 异常信息。
class SqlException : public Exception {
public:SqlException(const string& errmsg, int id, const string& sql): Exception(errmsg, id), _sql(sql) {}virtual string what() const override {return Exception::what() + "\n[SQL] " + _sql;}private:string _sql; // 错误关联的SQL语句
};// 缓存异常类,继承自 Exception
// CacheException 类:继承自 Exception 类,重写了 what() 函数,返回缓存异常信息
class CacheException : public Exception {
public:CacheException(const std::string& errmsg, int id): Exception(errmsg, id){}// 重写 what 函数,返回缓存异常信息virtual std::string what() const {std::string str = "CacheException:";str += _errmsg;return str;}
};// HTTP服务异常(具体错误类型)
// HttpServerException 类:继承自 Exception 类,添加了 _type 成员变量,
// 重写了 what() 函数,返回 HTTP 服务器异常信息。
class HttpServerException : public Exception {
public:HttpServerException(const string& errmsg, int id, const string& type): Exception(errmsg, id), _type(type) {}virtual string what() const override {return "[HTTP " + _type + " Error] " + Exception::what();}private:string _type; // 请求类型(GET/POST等)
}; what()函数的作用:what()函数是一个虚函数,在基类中定义,派生类可以重写该函数。通过基类指针或引用调用what()函数时,会根据实际对象的类型调用相应派生类的what()函数,实现多态。这样可以方便地根据不同的异常类型输出不同的异常信息。
2. 异常体系使用示例
// 模拟 SQL 管理函数,可能抛出 SqlException
void SQLMgr() {srand(time(0));if (rand() % 7 == 0) {throw SqlException("权限不足", 100, "select * from name = '张三'");}std::cout << "执行成功" << std::endl;
}// 模拟缓存管理函数,可能抛出 CacheException 或调用 SQLMgr 抛出 SqlException
void CacheMgr() {srand(time(0));if (rand() % 5 == 0) {throw CacheException("权限不足", 100);} else if (rand() % 6 == 0) {throw CacheException("数据不存在", 101);}SQLMgr();
}// 模拟 HTTP 服务器函数,可能抛出 HttpServerException 或调用 CacheMgr 抛出其他异常
void HttpServer() {srand(time(0));if (rand() % 3 == 0) {throw HttpServerException("请求资源不存在", 100, "get");} else if (rand() % 4 == 0) {throw HttpServerException("权限不足", 101, "post");}CacheMgr();
}// 主函数,捕获异常并处理
int main() {while (1) {Sleep(500);try {HttpServer();} catch (const Exception& e) { // 捕获基类异常对象,利用多态处理不同派生类异常std::cout << e.what() << std::endl;} catch (...) {std::cout << "Unkown Exception" << std::endl;}}return 0;
} 3. 常用异常编号
| 编号 | 异常描述 |
|---|---|
| 1 | 没有权限 |
| 2 | 服务器挂了 |
| 3 | 网络错误 |
七、常用标准库异常
bad_alloc:当使用new运算符进行动态内存分配失败时,会抛出bad_alloc异常。out_of_range:当使用容器(如std::vector、std::string等)的成员函数访问越界元素时,会抛出out_of_range异常。invalid_argument:当函数接收到无效的参数时,会抛出invalid_argument异常。
八、异常的优缺点
1. 优点
- 清晰准确的错误信息:异常对象可以定义丰富的信息,相比错误码方式,能更清晰准确地展示错误信息,甚至可以包含堆栈调用信息,有助于更好地定位程序的 bug。
- 简化错误处理流程:在函数调用链中,使用返回错误码的传统方式需要层层返回错误,最外层才能拿到错误信息并处理。而异常体系中,不管是深层函数还是中间层函数出错,抛出的异常会直接跳到合适的
catch块中,由调用者直接处理错误。//示例代码(错误码方式) #include <iostream> #include <errno.h>int ConnectSql() {// 用户名密码错误if (...) {return 1;}// 权限不足if (...) {return 2;}return 0; }int ServerStart() {if (int ret = ConnectSql() < 0) {return ret;}int fd = socket();if (fd < 0) {return errno;}return 0; }int main() {if (ServerStart() < 0) {// 处理错误}return 0; }//示例代码(异常方式) #include <iostream> #include <stdexcept>void ConnectSql() {// 用户名密码错误if (...) {throw std::runtime_error("用户名密码错误");}// 权限不足if (...) {throw std::runtime_error("权限不足");} }void ServerStart() {ConnectSql();int fd = socket();if (fd < 0) {throw std::system_error(errno, std::system_category(), "socket 错误");} }int main() {try {ServerStart();} catch (const std::exception& e) {std::cout << "捕获到异常: " << e.what() << std::endl;}return 0; }
- 与第三方库兼容:很多第三方库(如
boost、gtest、gmock等)都使用了异常机制,使用这些库时,我们也需要使用异常来与之配合。 - 适合特定函数:对于一些没有返回值的函数(如构造函数),或者不方便使用返回值表示错误的函数(如
T& operator[]),使用异常处理错误更加合适。
2. 缺点
- 执行流混乱:异常会导致程序的执行流乱跳,尤其是在运行时出错抛异常时,会使程序的控制流变得复杂,增加了跟踪调试和分析程序的难度。
- 性能开销:异常处理会有一定的性能开销,不过在现代硬件速度较快的情况下,这个影响通常可以忽略不计。
- 异常安全问题:C++ 没有垃圾回收机制,资源需要自己管理。使用异常容易导致内存泄漏、死锁等异常安全问题,需要使用 RAII(资源获取即初始化)技术来管理资源,增加了学习成本。
- 标准库异常体系混乱:C++ 标准库的异常体系定义不够完善,导致不同开发者各自定义自己的异常体系,使得代码的可维护性和兼容性受到影响。
- 异常规范问题:异常需要规范使用,否则会给外层捕获异常的用户带来困扰。异常规范主要包括两点:一是抛出的异常类型都继承自一个基类,二是函数是否抛异常、抛什么异常,都使用
func() throw();的方式进行规范化。
总结
异常总体而言利大于弊,在工程中鼓励使用异常。而且面向对象的编程语言基本都采用异常处理错误,这也是软件开发的趋势。
相关文章:
C++----异常
一、C 语言传统的错误处理方式 在 C 语言中,处理错误主要有两种传统方式,每种方式都有其特点和局限性。 1. 终止程序 原理:使用类似assert这样的断言机制,当程序运行到某个条件不满足时,直接终止程序的执行。示例代…...
合理规划时间,从容应对水利水电安全员考试
合理规划时间,从容应对水利水电安全员考试 在忙碌的工作与生活节奏中备考水利水电安全员考试,合理规划时间是实现高效备考的核心。科学的时间管理能让你充分利用每一分每一秒,稳步迈向考试成功。 制定详细的学习计划是第一步。依据考试时间…...
(解决) Windows 11使用SetSuspendState睡眠命令但是进入的是休眠
Windows 11 24H2 goes into hibernation mode instead of sleep mode. How can I create a sleep mode shortcut file? 25年3月4号 Win11 23H2 起因 使用网上说的睡眠命令创建bat双击后,电脑风扇会运行一段时间后再停止(应该是在保存进程到硬盘&#…...
Spring Boot 接口 JSON 序列化优化:忽略 Null 值的九种解决方案详解
一、针对特定接口null的处理: 方法一:使用 JsonInclude 注解 1.1 类级别:在接口返回的 DTO 类或字段 上添加 JsonInclude 注解,强制忽略 null 值: 类级别:所有字段为 null 时不返回 JsonInclude(Js…...
计算机毕业设计Python+DeepSeek-R1大模型考研院校推荐系统 考研分数线预测 考研推荐系统 考研(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
一、Prometheus架构
Prometheus 云原生十二要素是一套最佳实践和规范,旨在帮助开发人员更好地构建云原生应用 这十二个要素分别是: 单一职责独立部署无状态声明式API服务发现容错处理自适应算法自动化运维响应式编程通信协议服务注册与发现数据持久化一、Prometheus 是什么 Prometheus 是一个…...
火山引擎 DeepSeek R1 API 使用小白教程
一、火山引擎 DeepSeek R1 API 申请 首先需要三个要素: 1)API Key 2)API 地址 3)模型ID 1、首先打开火山引擎的 DeepSeek R1 模型页面 地址:账号登录-火山引擎 2、在页面右下角,找到【推理】按钮&#…...
react+vite+pnpm+ts基础项目搭建
1. 项目初始化 pnpm create vitelatest my-react-app --template react-ts cd my-react-app pnpm install2. 核心依赖安装 # 基础依赖 pnpm add react-router-dom tanstack/react-query zustand axios# UI 组件库 (任选其一) pnpm add mui/material emotion/react emotion/st…...
ArcGIS Pro 经纬网添加全解析:从布局到样式优化
在地理信息系统的广阔领域中,地图的精确性与直观性对于数据的呈现和分析起着至关重要的作用。 经纬网,作为地图上不可或缺的元素之一,能够为用户提供准确的地理坐标参考,帮助用户快速定位和理解地理空间数据的分布。 本文将深入…...
新闻研究导刊杂志社《新闻研究导刊》编辑部2024年第23期目录
研究论文 媒介智能化环境下新闻传播面临的风险及应对策略研究 冶玉娜; AI赋能地方政务新媒体智能化转型策略研究——以佛山政务新媒体为例 温秀妍; 新闻传播在社交媒体影响下的流变与发展展望 李晋; 县级融媒体中心生产优质短视频的路径探索 陈政清; 数字游…...
DDoS攻击的介绍和防治
一.DDoS攻击是什么 DDoS攻击:dos是服务器拒绝提供服务的意思,最前面的D是分布式的意思,所以说这个大概可以理解为分布式的机器攻击服务器,占用服务器资源,使得服务器拒绝提供服务的一种攻击手段,虽然原理简…...
UDP透传程序
UDP透传程序 本脚本用于在 设备 A 和 设备 B 之间建立 UDP 数据转发桥梁,适用于 A 和 B 设备无法直接通信的情况。 流程: A --> 电脑 (中继) --> B B --> 电脑 (中继) --> A 需要修改参数: B_IP “192.168.1.123” # 设备 B 的…...
深度学习pytorch之简单方法自定义9种卷积即插即用
本文详细解析了 PyTorch 中 torch.nn.Conv2d 的核心参数,通过代码示例演示了如何利用这一基础函数实现多种卷积操作。涵盖的卷积类型包括:标准卷积、逐点卷积(1x1 卷积)、非对称卷积(长宽不等的卷积核)、空…...
TMS320F28P550SJ9学习笔记2:Sysconfig 配置与点亮LED
今日学习使用Sysconfig 对引脚进行配置,并点亮开发板上的LED4 与LED5 我的单片机开发板平台是 LAUNCHXL_F28P55x 我是在上文描述的驱动库C2000ware官方例程example的工程基础之上进行添加功能的 该例程路径如下:D:\C2000Ware_5_04_00_00\driverlib\f28p…...
zRAM内存压缩技术:原理与实践初探
zRAM内存压缩技术:原理与实践指南 1. 技术背景与原理 zRAM是Linux内核中的一项内存压缩技术,于2014年进入Linux 3.14内核主线。它的核心思想是利用CPU压缩算法压缩内存数据,在不增加物理内存的情况下扩展系统有效内存容量。 当系统内存紧张…...
Hive 3.1 在 metastore 运行的 remote threads
Remote threads 是仅当 Hive metastore 作为单独的服务运行是启动,请求需要开启 compactor。 有以下几种: 1. AcidOpenTxnsCounterService 统计当前 open 的事务数 从表 TXNS 中统计状态为 open 的事务。此事务数量可以再 hive metrics 中。 2. Acid…...
大语言模型揭秘:从诞生到智能
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)无疑是技术领域最耀眼的明星之一。它们不仅能够理解人类的自然语言,还能生成流畅的文本,甚至在对话、翻译、创作等任务中表现出接近人类的智能…...
基于模糊PID控制的供热控制系统设计Simulink仿真
1.模型简介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2017Ra)软件。建议采用matlab2017 Ra及以上版本打开。(若需要其他版本可联系店主代为转换) 换热站干扰因素多导致传统PID控制无法满足控制要求的问题,提出利用…...
宝塔找不到php扩展swoole,服务器编译安装
1. 在php7.4中安装swoole,但找不到这个扩展安装 2. 服务器下载源码解压安装 http://pecl.php.net/package/swoole 下载4.8.0版本 解压到/www/server/php/74/下 3. 发现报错问题; 更新一下依赖 yum update yum -y install gcc gcc-c autoconf libjpe…...
LeetCode 1745.分割回文串 IV:动态规划(用III或II能直接秒)
【LetMeFly】1745.分割回文串 IV:动态规划(用III或II能直接秒) 力扣题目链接:https://leetcode.cn/problems/palindrome-partitioning-iv/ 给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
Linux信号保存与处理机制详解
Linux信号的保存与处理涉及多个关键机制,以下是详细的总结: 1. 信号的保存 进程描述符(task_struct):每个进程的PCB中包含信号相关信息。 pending信号集:记录已到达但未处理的信号(未决信号&a…...
RFID推动新能源汽车零部件生产系统管理应用案例
RFID推动新能源汽车零部件生产系统管理应用案例 一、项目背景 新能源汽车零部件场景 在新能源汽车零部件生产领域,电子冷却水泵等关键部件的装配溯源需求日益增长。传统 RFID 溯源方案采用 “网关 RFID 读写头” 模式,存在单点位单独头溯源、网关布线…...
