[C++]异常笔记
我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。
什么是C++的异常
在C++中,异常处理通常使用try-catch块来实现。try块用于包含可能会抛出异常的代码,而catch块用于捕获并处理异常。当异常被抛出时,程序会跳过try块中未执行的代码,并在catch块中执行适当的处理操作。如果没有抛出异常,则catch块将被跳过。
以下是一个简单的C++程序,演示了如何使用异常处理:
#include <iostream>int main() {try { // 尝试执行下面的代码块,如果发生异常,则跳转到catch块处理异常int x = 10; int y = 0; if (y == 0) { // 如果y等于0,抛出一个异常throw "Division by zero!"; // 抛出一个字符串类型的异常对象,内容为"Division by zero!"}int result = x / y; std::cout << "Result: " << result << std::endl; }catch (const char* error) { // 捕获一个字符串类型的异常对象,将异常对象赋值给变量errorstd::cerr << "Error: " << error << std::endl; // 输出错误消息,内容为"Error: Division by zero!"}return 0; }/* 在上面的程序中,try块包含了可能会抛出异常的代码, 包括将变量y赋值为0和使用除法运算符计算x除以y的结果。 如果y等于0,程序会抛出一个异常,内容为"Division by zero!"。 然后,catch块用于捕获并处理该异常,输出错误消息"Error: Division by zero!"。 */
异常对象
- 异常对象是一种特殊的对象。编译器依据异常抛出表达式构造异常对象(即异常对象总是被拷贝)。对象的类型是由表达式所表示对象的静态编译类型决定的。如Parent& rObj = Child; throw rObj;时会抛出Parent类型的异常对象。
- 异常对象存放在内存特殊位置,该位置既不是栈也不是堆,在Windows中是放在线程信息TIB中。该对象由异常机制负责创建和释放!(g++和vc下存储区域处理略有差异)。
- 异常对象不同于函数的局部对象,局部对象在函数调用结束后就被自动销毁,而异常对象将驻留在所有可能激活的catch语句都能访问到的内存空间中。当异常对象与catch语句成功匹配后,在该catch语句的结束处被自动析构。
- 在函数中返回局部变量的指针或引用几乎肯定会造成错误。同理,在throw语句中抛出局部变量的指针或引用也几乎是错误的。
// 捕获异常对象 (值,引用,指针) #include <iostream> #include <string> using namespace std;class MyException { public:MyException() { cout << "MyException():" << this << endl; }MyException(const MyException&) { cout << "MyException(const MyException&):" << this << endl; }~MyException() { cout << "~MyException():" << this << endl; }void what() { cout << "MyException: this = " << this << endl; } };class MyChildExcept : public MyException { public:MyChildExcept() { cout << "MyChildExcept():" << this << endl; }MyChildExcept(const MyChildExcept&) { cout << "MyChildExcept(const MyChildExcept&):" << this << endl; }~MyChildExcept() { cout << "~MyChildExcept():" << this << endl; }void what() { cout << "MyChildExcept: this = " << this << endl; } };void func_local() {// throw 局部对象MyException localEx;throw localEx; //尽管localEx是个局部对象,但这里会将其复制构造出一个异常对象,并存储在TIB中。而不是真正的将局部对象抛出去! }void func_temp() {//throw 临时对象MyException(); //临时对象1throw MyException(); //编译器会将这个临时对象直接存储在线程TIB中,成为异常对象(注意与临时对象1存储位置一般相距较远!) }void func_ptr() {//throw 指针throw new MyException(); //注意:异常对象是复制的堆对象而来的指针(存在内存泄漏风险!!!) }void func_again() {MyChildExcept child;MyException& re = child; //注意抛出的是re的静态类型的异常对象,即MyException,而不是MyChildExcept;throw re; }int main() {cout << "----------------------------------catch by value------------------------------" << endl;//按值捕获try {func_local(); //throw MyExecption()}catch (MyException e) { //复制异常对象,须额外进行一次拷贝!cout << "catch (MyException e)" << endl;e.what();}cout << "--------------------------------catch by reference----------------------------" << endl;//按引用捕获try {func_temp();}catch (MyException& e) { //直接引用异常对象,无须拷贝cout << "catch (MyException& e)" << endl;e.what();}cout << "---------------------------------catch by pointer-----------------------------" << endl;//按指针捕获try {func_ptr();}catch (MyException* e) { //按指针捕获(可能造成内存泄漏)cout << "catch (MyException* e)" << endl;e->what();delete e; //释放堆对象,防止内存泄漏}cout << "------------------------------------throw again-------------------------------" << endl;//二次抛异常try {try {func_again();}catch (MyException& e) {e.what();//注意以下两种方式不同//1. 在throw后指定异常对象为e//throw e; //e会继续复制一份,并抛出复制的异常对象而e本身会被释放!//2.throw后不指定任何对象,只要是在catch中捕获的,一律抛出去。throw; //此时,e本身再被抛出去。不会另外构造异常对象。}}catch (MyException& e) {e.what();}return 0; }
(参考博客:https://www.cnblogs.com/5iedu/p/11270922.html)
异常的抛出和捕获
异常的抛出和匹配原则
- 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
- 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
- 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
- catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
- 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,抛出的派生类对象,可以使用基类捕获,这个在实际中非常实用。
在函数调用链中异常栈展开匹配原则
- 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理。
- 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
- 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
- 4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
//栈展开的测试#include <iostream>// 自定义异常类,继承自std::exception类 class MyException : public std::exception { public:// 重写what()方法以返回异常信息字符串const char* what() const noexcept override {return "MyException: Something went wrong!";} };// 函数func3,抛出一个MyException异常 void func3() {std::cout << "func3: throwing MyException" << std::endl;throw MyException(); // 抛出一个MyException异常std::cout << "func3: return" << std::endl; //如果抛出异常,这里就不会执行}// 函数func2,调用函数func3 void func2() {std::cout << "func2: calling func3" << std::endl;func3(); // 调用函数func3std::cout << "func2: return" << std::endl; }// 函数func1,调用函数func2 void func1() {std::cout << "func1: calling func2" << std::endl;func2(); // 调用函数func2std::cout << "func1: return" << std::endl; }int main() {try {std::cout << "main: calling func1" << std::endl;func1(); // 调用函数func1}catch (const std::exception& e) { // 捕获一个std::exception类型的异常对象,将异常对象赋值给变量estd::cerr << "Exception caught: " << e.what() << std::endl; // 输出错误消息,内容为捕获的异常信息}return 0; // 程序结束 }
异常的重新抛出
有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。
namespace skate{// 服务器开发中通常使用的异常继承体系class Exception{public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}int getid() const{return _id;}protected:string _errmsg; // 描述错误信息int _id; // 错误编码// 堆栈信息};class HttpServerException : public Exception{public:HttpServerException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}private:const string _type;};void SeedMsg(const string& str){if (rand() < (RAND_MAX /4)*3){throw HttpServerException("SeedMsg::网络错误", 2, "put");}else if (rand() < RAND_MAX /3){throw HttpServerException("SeedMsg::你已经不是对方好友", 1, "post");}else{cout << "消息发送成功!->" << str << endl;}} }int main() {srand(time(0));while (1){::Sleep(1000);try{//skate::HttpServer();// 发送出现网络错误,要求重试3次// 权限错误就直接报错 for (size_t i = 0; i < 3; ++i){try{skate::SeedMsg("你好啊!今晚一起看电影怎么样?");break;}catch (const skate::Exception& e){if (e.getid() == 2) // 异常编码的价值,针对某个错误进行特殊处理{cout << "网络错误,重试发消息第" <<i+1<<"次"<< endl; //特殊处理if (2 == i) cout << "=======网络错误===发送失败======" << endl; //异常直接被捕获 不重新抛出 而是尝试重试continue; //网络错误,尝试重新发送 /} else // 其他错误{//break;//发送失败,直接重新抛出throw e; // 异常重新抛出 }}}}catch (const skate::Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl; //此处已经捕获不到 网络错误,因为网络错误没有重新抛出,已经被特殊处理了}catch (const std::exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0; }
《C++Primer》关于重新抛出
关于异常安全
- 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不 完整或没有完全初始化
- 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
- C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄 漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题。
补充关于构造函数与try语句块
关于异常规范(C++11 noexcept 声明)
C++0x与C++11异常规格声明方式的不同
- void func() throw() { ... } // throw()声明该函数不会产生异常(C++0x)
- void func() throw(int, double) { ... } //可能产生int或double类型异常(C++0x)
- void func() noexcept { ... } // noexcept声明该函数不会产生异常(C++11)
- void func() noexcept(常量表达式) { ... } //由表达式决定是否产生异常(C++11)
ps: 这里学习noexcept关键字的关键主要为了看得懂官方文档的声明,具体细节就做过多介绍了
-end
相关文章:
[C++]异常笔记
我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。 什么是C的异常 在C中,异常处理通常使用try-catch块来实现。try块用于包含可能会抛出异常的代码,而catch块用于捕获并处理异常。当异常被抛出时,程序会跳过try块中未执行…...
浅谈一级机电管道设计中的压力与介质温度
管道设计是工程设计中的一个非常重要的部分,管道的设计需要考虑到许多因素,其中就包括管道设计压力分类和介质温度分类。这两个因素是在设计管道时必须非常严格考虑的, 首先是管道设计压力分类。在管道设计中,根据工作要求和要传输…...
Docker网络模型(八)使用 macvlan 网络
使用 macvlan 网络 一些应用程序,特别是传统的应用程序或监控网络流量的应用程序,期望直接连接到物理网络。在这种情况下,你可以使用 macvlan 网络驱动为每个容器的虚拟网络接口分配一个MAC地址,使其看起来像一个直接连接到物理网…...
控制视图内容的位置
文本域中的提示内容在默认情况下是垂直居中的,要改变文本在文本域中的位置,可以使用android:gravity来实现。 利用android:gravity可以指定如何在视图中放置视图内容,例如,如何在文本域中放置文本。 如果希望视图文本显示在上方&a…...
【分布式系统与一致性协议】
分布式系统与一致性协议 CAP原理APCPCA总结BASE理论 一致性拜占庭将军问题 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。 分布式系统的设计目标一般包含如下: 可用性:可用性是分…...
音视频领域的未来发展方向展望
文章目录 音视频领域的未来发展方向全景音视频技术虚拟现实和增强现实的区别 人工智能技术可视化智能分析智能语音交互图像识别和视频分析技术 语音处理智能推荐技术远程实时通信 流媒体技术未来方向 音视频领域的未来发展方向 全景音视频技术:全景音视频技术是近年…...
时间同步/集群时间同步/在线/离线
目录 一、能够连接外网 二、集群不能连接外网--同步其它服务器时间 一、能够连接外网 1.介绍ntp时间协议 NTP(Network Time Protocol)网络时间协议,是用来使计算机时间同步的一种协议,它可以使计算机对其服务器或时钟源做同步…...
基于BP神经网络对MNIST数据集检测识别(numpy版本)
基于BP神经网络对MNIST数据集检测识别 1.作者介绍2.BP神经网络介绍2.1 BP神经网络 3.BP神经网络对MNIST数据集检测实验3.1 读取数据集3.2 前向传播3.3 损失函数3.4 构建神经网络3.5 训练3.6 模型推理 4.完整代码 1.作者…...
HTML5-创建HTML文档
HTML5中的一个主要变化是:将元素的语义与元素对其内容呈现结果的影响分开。从原理上讲这合乎情理。HTML元素负责文档内容的结构和含义,内容的呈现则由应用于元素上的CSS样式控制。下面介绍最基础的HTML元素:文档元素和元数据元素。 一、构建…...
Vue中Axios的封装和API接口的管理
一、axios的封装 在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSR…...
MLIR面试题
1、请简要解释MLIR的概念和用途,并说明MLIR在编译器领域中的重要性。 MLIR(Multi-Level Intermediate Representation)是一种多级中间表示语言,提供灵活、可扩展和可优化的编译器基础设施。MLIR的主要目标是为不同的编程语言、领域专用语言(DSL)和编译器…...
***杨辉三角_yyds_LeetCode_python***
1.题目描述: 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows …...
Mac使用DBeaver连接达梦数据库
Mac使用DBeaver连接达梦数据库 下载达梦驱动包 达梦数据库 在下载页面随便选择一个系统并下载下来。 下载下来的是zip的压缩包解压出来就是一个ISO文件,然后我们打开ISO文件进入目录:/dameng/source/drivers/jdbc 进入目录后找到这几个驱动包&#x…...
spring.expression 随笔0 概述
0. 我只是个普通码农,不值得挽留 Spring SpEL表达式的使用 常见的应用场景:分布式锁的切面借助SpEL来构建key 比较另类的的应用场景:动态校验 个人感觉可以用作控制程序的走向,除此之外,spring的一些模块的自动配置类,也会在Cond…...
从Cookie到Session: Servlet API中的会话管理详解
文章目录 一. Cookie与Session1. Cookie与Session2. Servlet会话管理操作 二. 登录逻辑的实现 一. Cookie与Session 1. Cookie与Session 首先, 在学习过 HTTP 协议的基础上, 我们需要知道 Cookie 是 HTTP 请求报头中的一个关键字段, 本质上是浏览器在本地存储数据的一种机制,…...
docker数据管理与网络通信
一、管理docker容器中数据 管理Docker 容器中数据主要有两种方式:数据卷(Data Volumes)和数据卷容器( DataVolumes Containers) 。 1、 数据卷 数据卷是一个供容器使用的特殊目录,位于容器中。可将宿主机的目录挂载到数据卷上,对数据卷的修改操作立刻…...
怎么查询电脑的登录记录及密码更改情况?
源头是办公室公用的电脑莫名其妙打不开了,问别人也都不知道密码是多少 因为本来就没设密码啊!(躺倒) 甚至已经想好了如果是50万想攻破电脑,被po抓住要怎么花这笔钱了 是我想太多 当然最后也没解决,莫名…...
《三》TypeScript 中函数的类型
TypeScript 允许指定函数的参数和返回值的类型。 函数声明的类型定义:function 函数名(形参: 形参类型, 形参: 形参类型, ...): 返回值类型 {} function sum(x: number, y: number): number {return x y } sum(1, 2) // 正确 sum(1, 2, 3) // 错误。输入多余的或者…...
深入学习 Mysql 引擎 InnoDB、MyISAM
tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。 💕💕 推荐:体系化学习Java(Java面试专题&#…...
【华为OD统一考试B卷 | 100分】阿里巴巴找黄金宝箱(V)(C++ Java JavaScript Python)
题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上面贴有一个数字。 阿里巴巴念出一个咒语数字k(k<N),找出连续k个宝箱数字和的最大值,并输出该最大值。 输入描述 第一行输入一个数字字串,数字之间…...
六步快速搭建个人网站
目录 第一步、选择搭建平台WordPress 第二步、选域名 1)域名在哪买? 2)域名怎么选? 3)以阿里云为例,讲解怎么买域名 第三步、选择服务器 第四步、申请主机、安装WordPress 第五步、选择WordPress模…...
TypeScript 中的 type 关键字有什么用?
创建类型别名 在 TypeScript 中,type 关键字用于创建类型别名(Type Alias)。类型别名可以给一个类型起一个新的名字,使代码更具可读性和可维护性。 类型别名可以用于定义各种类型,包括基本类型、复合类型和自定义类型…...
27 getcwd 的调试
前言 同样是一个 很常用的 glibc 库函数 不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 获取当前路径 不过 我们这里是从 具体的实现 来看一下 测试用例 就是简单的使用了一下 getcwd rootubuntu:~/Desktop/linux/HelloWorld# cat Test04Getcwd.c #inc…...
使用IDEA使用Git:Git使用指北——实际操作篇
Git使用指北——实际操作 🤖:使用IDEA Git插件实际工作流程 💡 本文从实际使用的角度出发,以IDEA Git插件为基座讲述了如果使用IDEA的Git插件来解决实际开发中的协作开发问题。本文从 远程仓库中拉取项目,在本地分支进行开发&…...
java boot将一组yml配置信息装配在一个对象中
其实将一组yml数据封进一个对象中才是以后的主流开发方式 我们创建一个springboot项目 找到项目中的启动类所在目录 在同目录下创建一个类 名字你们可以随便取 我这里直接叫 dataManager 然后 在yml中定义这样一组数据信息 然后 我们在类中定义三个和这个配置信息相同的字段…...
【裸机开发】链接脚本(.lds文件)的基本语法
目录 一、什么是链接脚本? 二、链接脚本的基本语法格式 1、常用命令 2、内置变量 三、链接脚本的简单案例 一、什么是链接脚本? 一段程序的编译需要经历四个阶段(预处理—编译—汇编—链接),而链接脚本管理的就是…...
Java 进阶 -- 集合(三)
4、实现 实现是用于存储集合的数据对象,它实现了接口部分中描述的接口。本课描述了以下类型的实现: 通用实现是最常用的实现,是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。特殊目的实现是为在特殊情况下使用而设计的࿰…...
【华为OD机试真题 C语言】5、TLV解析 | 机试真题+思路参考+代码解析
文章目录 一、题目🎃题目描述🎃输入输出🎃样例1 二、思路参考三、代码参考🏆C语言 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 华为OD机试真题汇总,定期…...
(七)CSharp-刘铁锰版-事件
一、初步了解事件 定义:单词 Event ,译为“事件” 《牛津词典》中的解释是“a thing that happens,especially something important”通顺的解释就是“能够发生的什么事情” 角色: 使对象或类具备通知能力的成员 (中译&#x…...
【ROS】郭老二博文之:ROS目录
1、ROS2 【ROS】Ubuntu22.04安装ROS2(Humble Hawksbill) 【ROS】ROS2命令行工具详解 【ROS】ROS2中的概念和名词解释 【ROS】ROS2编程示例:话题订阅-发布-C版 【ROS】ROS2编程示例:服务和客户端-C版 【ROS】ROS2编程示例…...
网站图片的作用/如何优化网站快速排名
我们知道python下的多进程做异步还是可以的,但是做并发利用多核处理器是行不通的,而且速度还会更慢。那么我们来试试多进程的效果吧。简单看下多进程的几种实现方法。 1. 普通进程启动与测试 #!/usr/bin/env python #################################…...
那种导航网站/百度手机极速版
其实是基本操作。。。。但我懒得每次都去想一遍,就记录一下。 问题是这样的比如我现在有一个矩阵是46*22的,但是我想要把它变成一维的1012*1。就很简单. [x,y]size(Bw); %确定矩阵维度 Brezeros(x*y,1); for i1:yfor j1:xBre((i-1)*xj)Bw(j,i); e…...
做公益网站怎么赚钱/推广普通话文字内容
一、DNS:域名系统。 主要用来记录(登记)域名和IP地址的映射关系(对应关系)。 功能:域名解析。 正向解析:根据域名来解析出IP地址。域名----->IP 反向解析:根据IP地址来解析出域名。IP----->域名 ---------------------…...
网站托管是什么/医院营销策略的具体方法
再游洛带有感——代腾飞 2007年9月15日 于成都今游古镇到洛带恍然忽至回唐朝身穿古装成侠客浪迹江湖甚逍遥...
网站建设实训报告心得体会/社群营销
MySQL - 扩展性 1 概述:人多力量未必大我们应该接触过或者听说过数据库的性能瓶颈问题。对于一个单机应用而言,提升数据库性能的最快路径就是氪金 - 买更高性能的数据库服务器,只要钱到位,性能不是问题。 但是当系统性能增加到一定…...
可信的大连网站建设/地推接单平台
centos7全面升级了引导程序和系统管理程序,使用grub2替代了grub来引导操作系统,使用systemd替换了init管理系统程序,systemd的升级比较激进,从架构上变更init管理程序。微服务docker却希望使用docker domean管理微服务中的进程&am…...