【C++】异常处理机制(对运行时错误的处理)

75e194dacf184b278fe6cf99c1d32546.jpeg

🌈 个人主页:谁在夜里看海.

🔥 个人专栏:《C++系列》《Linux系列》

⛰️ 天高地阔,欲往观之。

d047c7b1ef574257b8397fe5cc5c290b.gif

目录

引言

1.编译器可以处理的错误

2.编译器不能处理的错误

3.传统的错误处理机制

assert终止程序

返回错误码 

一、异常的概念

直观的例子

二、异常的使用

1.异常的抛出和捕获

抛出异常 

捕获异常

处理异常 

2.异常的重新抛出

三、异常的安全与规范

1.异常安全

2.异常规范

3.自定义异常体系

四、异常的优缺点


引言

我们在编写程序的时候不可避免地会造成一些错误,有些错误是编译器可以帮我们找出并纠正的,而有些错误则需要我们自己自行处理。

1.编译器可以处理的错误

一般是一些静态的语义和语法错误,下面列举一些常见的错误:

// 1.语法错误
int a = 0 // 缺少封号// 2.类型错误
int b = "hello"; // 类型不匹配// 3.未定义标识符
int result = unknownFunc(); // 未定义的函数// 4.作用域错误
if(1){int x = 10;
}
cout << x; // 不在当前作用域// ......

2.编译器不能处理的错误

编译器无法检测和处理的错误主要是程序运行时才会发生的动态错误,例如下面几种:

// 1.运行时错误(除零、非法访问内存、堆栈溢出)
int a = 10;
int b = 0;
int c = a / b;  // 运行时的除零错误// 2.逻辑错误,逻辑错误是在代码设计或实现上出现的错误,但从语法和类型上都是合法的。
// 逻辑错误只能通过调试和测试来发现。例如,意图将数字从1到10累加,但写错了循环条件:
int sum = 0;
for (int i = 1; i <= 10; ++i) {sum -= i;  // 应该是 sum += i
}// 3.资源管理错误,包括内存泄露等问题,编译器无法检测
int* ptr = new int[10]; // 没有 delete[] ptr;// ......

总上所述,编译器负责静态检测,只能检查代码中明显的语义和语法错误对于运行时的错误处理则需要我们设置适当的错误处理机制

3.传统的错误处理机制

assert终止程序

assert是C和C++中的一个宏,用于在程序中检查条件是否满足,如果条件不满足,assert会报错并终止程序,可以在错误发生的地方及时发现问题:

int operation()
{int a = 0;int b = 0;cin >> a >> b; // 输入 3 0// assert判错assert(b); // 此时发现错误,程序终止return a / b;
}int main()
{operation();return 0;
}

弊端:

在调试过程中,assert还是很有用的,但是在发布版本(Release模式)下会降低性能,因此会将其禁用。通过定义宏 NDEBUG 禁用 assert:

#define NDEBUG
#include <cassert>

而且在一些情况下,使用assert终止程序会发生很严重的错误!

实际上C语言基本是使用返回错误码的方式处理错误:

返回错误码 

在C语言中,函数通常返回一个整数表示执行的结果:

返回0表示执行成功;

返回非零值(整数或负数)表示不同的错误类型,根据错误码,调用者可以判断错误类型 

常见的错误码有以下类型:

  • 标准错误码:使用标准库中定义的宏,如 EXIT_SUCCESSEXIT_FAILURE:
#include <stdlib.h>
#include <stdio.h>int divide(int a, int b) {if (b == 0) {return EXIT_FAILURE;  // 返回标准错误码表示失败}printf("Result: %d\n", a / b);return EXIT_SUCCESS;  // 返回标准错误码表示成功
}int main() {if (divide(10, 0) == EXIT_FAILURE) {printf("Error: Division by zero.\n");}return 0;
}
  • 自定义错误码:为程序中的不同错误类型定义特定的错误码。
#include <stdio.h>#define SUCCESS 0
#define ERR_DIVISION_BY_ZERO -1
#define ERR_INVALID_INPUT -2int divide(int a, int b, int* result) {if (b == 0) {return ERR_DIVISION_BY_ZERO;  // 返回自定义错误码}*result = a / b;return SUCCESS;  // 返回成功码
}int main() {int result;int status = divide(10, 0, &result);if (status == ERR_DIVISION_BY_ZERO) {printf("Error: Division by zero.\n");} else if (status == SUCCESS) {printf("Result: %d\n", result);}return 0;
}

弊端:

每次调用函数都要检查返回值,如果嵌套调用较多,会造成大量错误检查代码,降低代码可读性,我们需要一种更简洁更灵活的错误管理机制,它就是C++异常处理:

一、异常的概念

异常是指程序运行时发生的、使程序无法正确执行的错误或异常情况,而异常处理机制是一种处理异常的手段,它允许程序在遇到问题时转移到异常处理逻辑,而不是直接崩溃。

直观的例子


对异常不处理:

int main()
{int a = 0;int b = 0;cin >> a >> b; // 输入 3 0cout << a / b << endl; // 发生除零异常,程序中断return 0;
}


使用assert处理:

int main()
{int a = 0;int b = 0;cin >> a >> b;//	 assert处理assert(b);cout << a / b << endl;return 0;
}


使用异常处理机制:

int main()
{int a = 0;int b = 0;cin >> a >> b;//	 异常处理机制try{if (b == 0){throw "The dividend is zero";}}catch (const char* ret){cout << ret << endl;return 0; // 遇到除零 结束程序}cout << a / b << endl;return 0;
}

我们可以看到,相比于assert,异常处理可以更直观地显示错误信息,而不是让程序崩溃或无反应

下面介绍异常的使用方法:

二、异常的使用

1.异常的抛出和捕获

抛出异常 

当程序遇到无法正常处理的问题或错误情况时,使用 throw 语句抛出异常。抛出的异常对象可以是一个具体的值(如整数、字符串)或特定的异常类实例:

	int a = 0;int b = 0;cin >> a >> b;// 异常处理机制try{if (b == 0){throw "The dividend is zero";}}

在这里,如果b == 0(除零错误),则抛出一个 char* 类型的错误信息,提示“除零错误”。

捕获异常

当抛出异常后,程序会自动寻找最接近的 catch来捕获这个异常。在 catch 块中,可以编写处理代码以应对错误。多个 catch 块可以处理不同类型的异常。

int main()
{int a = 0;int b = 0;while (1){cin >> a >> b;// 异常处理判错try {if (b < 0) {string info = { "The dividend is negative" }; // 除负数(假定为错误)throw info;}if (b == 0){throw "The dividend is zero"; // 除零错误}}catch (const string info) { // 捕获string类型的异常cout << info << endl;}catch (const char* info){ // 捕获char*类型的异常cout << info << endl;}}return 0;
}

try 块中调用 divide 函数,如果发生异常,程序会跳转到相应的 catch,输出捕获到的异常信息。

处理异常 

catch 块中处理异常的逻辑可以包括打印错误信息、清理资源、执行恢复操作等。这样可以防止程序崩溃,并在必要时继续执行其他代码。 

多类型异常捕获:

 有时候可能需要处理多种类型的异常,这时可以使用多个 catch 块,就像上面那样,也可以使用一个通用的 catch(...) 块来捕获所有异常。

注意:

 catch(...) 可以捕获任意类型的异常,但同时并不能清楚异常的类型,所以一般是放在程序的最后,用于捕获未知异常的,相当于一个保障,并且不能放在其他异常捕获代码的前面,否则会屏蔽其他异常捕获:

2.异常的重新抛出

有时单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理:

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.异常安全

由于异常处理会导致程序执行流程的随意跳转,过多或任意地使用异常可能会导致代码混乱,而且随意抛出异常会有资源泄露的风险,所以下面几种情况最好不要抛出异常:

1. 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化

2. 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内 存泄漏、句柄未关闭等)

3. 在new和delete中最好不要抛出异常,可能会导致内存泄漏

4. 在lock和unlock中最好不要抛出异常,可能会导致死锁

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;

3.自定义异常体系

在实际中,公司会自定义自己的异常体系进行规范的异常管理,通常会定义一套继承的规范体系,这样大家抛出的都是继承的派生类对象,都只要捕获基类对象就可以了(C++允许通过基类对象捕获其派生类对象)

在 C++ 中通常继承 std::exception 类(标准异常类):

#include <exception>
#include <string>
using namespace std;// 基类对象
class Custom : public exception {
protected:string message; // 错误信息int errorCode; // 错误码public:Custom=(const =string& msg, int code = 0): message(msg), errorCode(code) {}virtual const char* what() const noexcept override {return message.c_str();}int getErrorCode() const { return errorCode; }
};// 派生类对象
class Database= : public Custom {
public:Database(const string& msg, int code = 1001): Custom("Database Error: " + msg, code) {}
};class Network : public Custom {
public:Network(const string& msg, int code = 1002): Custom("Network Error: " + msg, code) {}
};

四、异常的优缺点

异常的优缺点如下:

                                优点                                    缺点
1.清晰准确的展示出错误的各种信息1.导致程序的执行流乱跳,跟踪调试时比较困难
2.直接跳转catch捕获,直接处理错误2.额外的性能开销(可忽略)
3.使用场景更为广泛3.内存泄漏的风险(可结合智能指针解决)
4.需要手动定义标准体系

总结:异常总体还是利大于弊的,在工程中也鼓励使用异常。 


以上就是对异常处理的介绍与个人理解,欢迎指正~ 

码文不易,还请多多关注支持,这是我持续创作的最大动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467573.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLI LABS | Less-35 GET-Bypass Add Slashes (we dont need them) Integer Based

关注这个靶场的其它相关笔记&#xff1a;SQLI LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;过关流程 输入下面的链接进入靶场&#xff08;如果你的地址和我不一样&#xff0c;按照你本地的环境来&#xff09;&#xff1a; http://localhost/sqli-labs/Less-35/ 话不多说…

【Qwen2技术报告分析】解读模型架构 pre/post数据构建和模型评估

目录 前言 一、Tokenizer 二、模型结构 dense模型 MoE模型 模型参数设置 三、Pre-Training Pre-Training DATA LONG-CONTEXT TRAINING 四、Post-Training Post-Training DATA 人工数据注释&#xff08;collaborative data annotation&#xff09; 自动数据合成&a…

【HarmonyOS】not supported when useNormalizedOHMUrl is not true.

【HarmonyOS】 not supported when useNormalizedOHMUrl is not true. 问题背景&#xff1a; 集成三方库编译时&#xff0c;IDE提示报错信息如下&#xff1a; hvigor ERROR: Bytecode HARs: [cashier_alipay/cashiersdk] not supported when useNormalizedOHMUrl is not true…

pdb和gdb的双剑合璧,在python中调试c代码

左手编程&#xff0c;右手年华。大家好&#xff0c;我是一点&#xff0c;关注我&#xff0c;带你走入编程的世界。 公众号&#xff1a;一点sir&#xff0c;关注领取python编程资料 问题背景 正常情况下&#xff0c;调试python代码用pdb&#xff0c;调试c代码用gdb&#xff0c;…

基于MPPT最大功率跟踪的光伏发电蓄电池控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MPPT最大功率跟踪的光伏发电蓄电池控制系统simulink建模与仿真。本系统包括PV模块&#xff0c;电池模块&#xff0c;电池控制器模块&#xff0c;MPPT模块&#xff0c;PWM模…

uni-app打包后报错云服务空间未关联

使用uni-app打包到h5 项目里面用到了uni-app的云端一体城市选择组件&#xff0c;这个组件数据用到了uniCloud云服务空间&#xff0c;在本地运行没问题&#xff0c;打包之后测试环境报错&#xff1a; 一顿查&#xff0c;查到了官网是这样说的&#xff1a; cli publish --platfo…

vue用jenkins 打包项目项目关闭eslint检查

问题描述&#xff1a;创建vue脚手架项目后&#xff0c;使用jenkins 打包项目&#xff0c;出现如下图所示错误&#xff0c;显示错误来源于eslint检测。 解决方法&#xff1a;在根目录下找到vue.config.js文件&#xff0c;添加lintOnSave: false以关闭eslint检测&#xff0c;项目…

基于Spring Boot的美术馆管理系统的设计与实现,LW+源码+讲解

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统美术馆管理系统信息管理难度大&#xff0c;容错率低&…

战略共赢 软硬兼备|云途半导体与知从科技达成战略合作

2024年11月5日&#xff0c;江苏云途半导体有限公司&#xff08;以下简称“云途”或“云途半导体”&#xff09;与上海知从科技有限公司&#xff08;以下简称“知从科技”&#xff09;达成战略合作&#xff0c;共同推动智能汽车领域高端汽车电子应用的开发。 云途半导体与知从科…

【TMM2024】Frequency-Guided Spatial Adaptation for Camouflaged Object Detection

论文链接&#xff1a;https://arxiv.org/abs/2409.12421 这个论文研究 Camouflaged Object Detection &#xff08;COD&#xff09;问题&#xff0c;作者认为&#xff0c;使用 pretrained foundation model 可以改进COD的准确率&#xff0c;但是当前的 adaptor 大多学习空间特…

前端环境配置

对于换公司的小伙伴来讲&#xff0c;重新安装环境&#xff0c;百度或许稍微有点麻烦&#xff0c;本文章让你无脑式直接操作&#xff0c;保证环境畅通无阻。 1.安装nvm-setup 该插件是一款管理nodeJs的包&#xff0c;无需你单独下载nodeJs去安装&#xff0c;只需要下载安装此…

window中借助nginx配置vite+vue项目的反向代理步骤

在官网下载好nginx的安装包后&#xff0c;解压后 CMD打开 start nginx 是启动命令 nginx -s stop 停止服务 nginx -s reload 如果重写了nginx.conf文件&#xff0c;要执行这条命令 正常情况下 成功启动和成功停止服务长这样 错误情况&解决 如果nginx -s stop失败 ngi…

论文阅读:基于语义分割的非结构化田间道路场景识别

论文地址&#xff1a;DOI: 10.11975/j.issn.1002-6819.2021.22.017 概要 环境信息感知是智能农业装备系统自主导航作业的关键技术之一。农业田间道路复杂多变&#xff0c;快速准确地识别可通行区域&#xff0c;辨析障碍物类别&#xff0c;可为农业装备系统高效安全地进行路径规…

【Ant Design Pro】如何实现组件的状态保存umi-plugin-keep-alive插件的使用

都知道vuejs里面帮我们实现了一个内置的keep-alive组件&#xff0c;给我们缓存一些组件的状态带来了很大的便利。但是在react中没有自带的实现&#xff0c;可以借助社区的插件umi-plugin-keep-alive来实现这个功能。 实现效果对比 未使用插件&#xff0c;可以看到我们在页面跳…

Amesim中PID控制元件

PID 控制原理 PID 即比例&#xff08;Proportional&#xff09;、积分&#xff08;Integral&#xff09;、微分&#xff08;Derivative&#xff09;控制。比例环节根据偏差的大小成比例地对系统进行调节&#xff0c;偏差越大&#xff0c;调节作用越强。积分环节用于消除系统的…

SpringBoot框架:共享汽车行业的技术革新

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了共享汽车管理系统的开发全过程。通过分析共享汽车管理系统管理的不足&#xff0c;创建了一个计算机管理共享汽车管理系统的方案。文章介绍了共享汽车管理系统的系…

ASP.NET Core 路由规则,自定义特性路由 ,IActionConstraint 路由约束 总结 mvc

资料 资料 路由服务 路由服务是在 Program.cs 中使用 builder.Services.AddRouting()注册的&#xff0c; 只是默认在 builder 之前已经注册过了&#xff0c;无需我们再次注册。 AddRouting()方法必须在 UseRouting()方法之前运行&#xff0c;它是路由的基础服务。 MapContro…

linux基础——详细篇

免责声明 学习视频来自B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 linux 基础命令重现 cd(切…

Prosre:一款直观的协议发送模拟软件

Proser 是一款直观的协议编辑、发送端模拟软件。 在涉及二进制协议通信的程序开发过程中&#xff0c;我们经常会通过助手类工具编写协议来验证自己的代码&#xff0c;但这些助手对于大协议的编辑非常不友好&#xff0c;这时Proser会协助你轻松的完成测试。 特点 数据直接表达…

常见 HTTP 状态码分类和解释及服务端向前端返回响应时的最完整格式

目前开发的项目很大程度上是为明年的国产化做准备了&#xff0c;所以借这个机会把用了十年的自研系统全部重写&#xff0c;订立更严格的规范&#xff0c;本文记录一下返回格式及对应状态码。 常见 HTTP 状态码及解释 HTTP 状态码用于表示客户端请求的响应状态&#xff0c;它们…