[C++]异常笔记

news/2024/9/15 21:30:33/

         我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。        

什么是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


http://www.ppmy.cn/news/325713.html

相关文章

4.LVS负载均衡集群

文章目录 LVS负载均衡集群集群介绍集群类型LVS工作模式LVS虚拟服务器介绍LVS的NAT模式部署设置NFS服务器设置节点服务器配置负载调度器 LVS负载均衡集群 集群介绍 群集的含义 Cluster&#xff0c;集群、群集由多台主机构成&#xff0c;但对外只表现为一个整体&#xff0c;只提…

从零玩转系列之微信支付实战基础框架搭建

一、前言 halo各位大佬很久没更新了最近在搞微信支付,因商户号审核了我半个月和小程序认证也找了资料并且将商户号和小程序进行关联,至此微信支付Native支付完成.此篇文章过长我将分几个阶段的文章发布(项目源码都有,小程序和PC端) 在此之前已经更新了微信支付开篇、微信支付安…

AD09 PCB拼板制作完整流程

1、新建PCB文件 画好PCB之后&#xff0c;先在原有工程文件下新建一个PCB文件&#xff08;可以保存为XXX拼板&#xff09;&#xff0c;PCB大小根据拼板前大小以及拼板阵列确定&#xff08;也可以在做好工艺边后再修改大小&#xff09;。 2、放置PCB阵列 如上图&#xff0c;来…

Nginx扩展篇之Location语法规则

1 Location语法规则 1.1 Location规则 语法规则&#xff1a; location [||*|^~] /uri/ {… } 首先匹配 &#xff0c;其次匹配^~,其次是按文件中顺序的正则匹配&#xff0c;最后是交给 /通用匹配。当有匹配成功时候&#xff0c;停止匹配&#xff0c;按当前匹配规则处理请求。 …

二叉树的递归如何写

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 笔者在一文读懂二叉树中有介绍到二叉树的深度优先搜索中讲到&#xff1a;前序遍历、中序遍历、后续遍历&#xff0c;今天我就用代码的形式给大家展示出来。 首先大家…

【LeetCode】HOT 100(8)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

如何提高固态硬盘读取速度?

虽然固态硬盘的读取速度已经很快了&#xff0c;但是我们可以在系统中简单设置一下让固态硬盘的读取速度更快起来 首先右键点击“此电脑”选择属性 点击设备管理器 在磁盘驱动器中右键点击固态硬盘&#xff0c;选择属性 选择策略&#xff0c;勾选“启用设备上的写入缓存”&…

trim有什么用计算机二级,玩转SSD中的TRIM技术

现在SSD不论磁盘可用空间是否充足&#xff0c;对SSD的性能影响几乎可忽略不计。这其实是TRIM的功劳&#xff0c;你了解它吗&#xff1f; 高效SSD必备技术 我们在使用普通HDD时Windows会自动在一定时间段去整理磁盘碎片&#xff0c;但如果你的SSD支持TRIM的话是无需碎片整理的。…

SSD详解(二)

原文链接&#xff1a;点击打开链接 算法概述 本文提出的SSD算法是一种直接预测目标类别和bounding box的多目标检测算法。 与faster rcnn相比&#xff0c;该算法没有生成 proposal 的过程&#xff0c;这就极大提高了检测速度。针对不同大小的目标检测&#xff0c;传统的做…

SSD内部详解

1、ssd的基本架构 直接上图&#xff0c;给出一个简单SSD的内部基本架构 从这个图中可以看到FTL层主要是三个功能&#xff1a;地址映射表、损耗均衡、垃圾回收 地址映射表&#xff1a;顾名思义&#xff0c;把文件系统的逻辑地址&#xff0c;映射到flash的物理地址上。 损耗均…

SSD算法详解

SSD github : https://github.com/weiliu89/caffe/tree/ssd SSD paper : https://arxiv.org/abs/1512.02325 SSD 动态PPT&#xff1a; https://docs.google.com/presentation/d/1rtfeV_VmdGdZD5ObVVpPDPIODSDxKnFSU0bsN_rgZXc/pub?startfalse&loopfalse&delayms3000…

SSD_学习笔记记录

one-steage VGG16模型的修改 VGG16的模型输出&#xff0c;其中224.。。为特征图的大小。 SSD模型中对VGG网络的修改&#xff1a; SSD模型是对VGG中的第四个卷积块中的最后一个Conv2d进行第一个输出&#xff0c;在这里简称为Conv4-3。以及第五个卷积块中的最后一个Conv2d进行…

SSD为什么需要Trim?

什么是Trim&#xff1f; Trim又叫 Disable Delete Notify。当系统删除某个文件时候&#xff0c;它只是简单的在逻辑数据表内把存储要删除的数据的位置标记为可用而已。使用机械硬盘的系统根本就不需要向存储设备发送任何有关文件删除的消息&#xff0c;因为在将来&#xff0c;…

SSD固态硬盘的Trim命令是什么

SSD固态硬盘的Trim命令是什么 我相信听到过买SSD要买支持Trim的玩家不在少数&#xff0c;不过我敢肯定大多数玩家到目前为止还搞不懂Trim到底是干啥的&#xff0c;只是一味跟风&#xff0c;今天在这里我就稍微详细的解释下Trim这个命令。 Q: 为啥我们需要Trim&#xff1f; A…

固态硬盘SSD学习笔记:SSD综述

一. SSD VS HDD SSD(Solid State Device)&#xff1a;闪存介质主控 HDD(Hard Disk Drive)&#xff1a;马达磁头磁盘 方式数据存储介质读取写入HDD机械磁盘&#xff08;磁性介质&#xff09;磁头马达&#xff08;寻址&#xff09;SSD电子闪存SSD控制器 SSD优点&#xff1a;性…

[SSD核心技术:FTL 6] 固态硬盘缓存(DRAM)对性能的影响机制

​ 声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 目录 前言名词解释1. SSD 缓存1.1 DRAM SSD1.2 无DRAM SSD1.3 HMB SSD1.4 有无DRAM 的 SSD 对比前言 我给祖国庆个生,分享下存储芯片国产进展。 存储芯片生态…

[深入理解SSD 为SSD编程] SSD的架构和基准

声明 主页&#xff1a; 元存储的博客_CSDN博客 依公开知识及经验整理&#xff0c;如有误请留言。 个人辛苦整理&#xff0c;付费内容&#xff0c;禁止转载。 内容摘要 前言 1. SSD的架构 1.1 NAND闪存单元 1.2 SSD的组织 1.3生产过程 2.基准和性能度量 2.1基本基准 2.2 预处理 …

浅谈SSD Trim(Data set management)

SSD TRIM TRIM 作为消费级SSD的救世神药&#xff0c;也是性能起飞的催化剂&#xff0c;下面简单介绍TRIM的前世今生。 一. TRIM相关背景/TRIM需要解决的问题 TRIM由文件系统发起&#xff0c;就拿FAT32文件系统举例&#xff0c; 一个文件包括两个部分file&#xff08;文件指针&…

[SSD核心技术:FTL 16] 固态硬盘预读技术详解

声明 主页: 元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 什么是预读? 预读原理 基于历史IO请求的预读技术 随机读操作预读技术 混合数据流的分