当前位置: 首页 > news >正文

【C++进阶9】异常

在这里插入图片描述

一、C语言传统的处理错误的方式

  1. 终止程序,如assert
    如发生内存错误,除0错误时就会终止程序
  2. 返回错误码
    需要程序员自己去查找对应的错误
    z如系统的很多库的接口函数都是通
    过把错误码放到errno中,表示错误

二、C++异常概念

异常:函数无法处理的错误就可以抛出异常
让函数的直接或间接的调用者处理这个错误

  • throw: 出现问题时,程序抛出一个异常
    通过使用 throw 关键字来完成
  • catch: 在您想要处理问题的地方
    通过异常处理程序捕获异常 .catch 关键字用于捕获异常
    可以有多个catch进行捕获
  • try: try 块中的代码标识将被激活的特定异常
    它后面通常跟着一个或多个 catch 块

使用方法

double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}
void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main()
{try{Func();}catch (const char* str) // catch 得跟"Division by zero condition!"类型匹配{cout << str << endl; // Division by zero condition!}return 0;
}

2.1 异常的抛出和匹配原则

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码
  2. 选中的处理代码是调用链中与该对象类型匹配离抛出异常位置最近的那一个
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,
    所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁
    (这里的处理类似于函数的传值返回)
  4. catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么
  5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用

正常的异常抛出包含
错误码和错误描述

class Exception
{
public:Exception(int errid, const string& msg): _errid(errid), _errmsg(msg){}const string& GetMsg() const // 引用返回,成员变量出了作用域还在{return _errmsg;}int GetErrid() const{return _errid;}private:int _errid; // 错误码string _errmsg; // 错误描述
};double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){// Exception err(1, "除0错误");// throw err;throw Exception(1, "除0错误"); // 用匿名对象,连续的构造+拷贝编译器会优化}else{return ((double)a / (double)b);}
}void Func()
{int len, time;cin >> len >> time;try {cout << Division(len, time) << endl;}catch (char str){cout << str << endl;}cout << "void Func()" << endl;
}int main()
{try{Func();}catch (const Exception& e) {cout << e.GetMsg() << endl;}catch (...) // 捕获任意类型的异常,一般放到最后,防止有些异常没捕获到,导致程序终止{cout << "未知异常" << endl; }return 0;
}

一个公司里面每个人对抛异常的需求不一样
有些人可能只需要一个错误码
有些人则需要更详细的信息

解决方法: 可以搞一个继承体系
比如你是缓存部分我是网络部分
可以写一个子类继承父类Exception
捕获的时候捕两个部分
一个Exception
一个是未知异常
C++很灵活的地方抛子类捕父类

三、自定义异常体系

实际使用中很多公司
都会自定义自己的异常体系
进行规范的异常管理
因为一个项目
如果大家随意抛异常
那么外层的调用者基本没法玩
所以实际中会定义一套继承的规范体系
这样大家抛出的都是继承的派生类对象
捕获一个基类就可以了
在这里插入图片描述
服务器开发中通常使用的异常继承体系

class Exception
{
public:Exception(int errid, const string& msg): _errid(errid), _errmsg(msg){}virtual string what() const // 引用返回,成员变量出了作用域还在{return _errmsg;}int GetErrid() const{return _errid;}protected: // 子类可见用保护int _errid; // 错误码string _errmsg; // 错误描述
};class SqlException : Exception // sql专属继承
{
public:SqlException(int errid, const string& msg, const string& sql) // 派生类必须调用父类的构造函数,派生类的特点必须得去复用父类: Exception(errid, msg), _sql(sql){}virtual string what() const // exception写成虚函数进行重写,构造需要的错误信息进行返回{string msg = "SqlException:";msg += _errmsg;msg += "->";msg += _sql;return msg;}protected:string _sql;
};// 出现数据库错误或网络错误
class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(id, errmsg){}virtual string what() const{string msg = "CacheException:";msg += _errmsg;return msg;}
};class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type) // 外加一个类型错误:Exception(id, errmsg), _type(type){}virtual string what() const{string msg = "HttpServerException:";msg += _errmsg;msg += "->";msg += _type; return msg;}private:const string _type;
};void SQLMgr() // 数据库
{srand(time(0));if (rand() % 7 == 0){throw SqlException(100, "权限不足", "select * from name = '张三'");}cout << "调用成功" << endl; // 3.数据库出错抛异常,数据库没出错,调用成功.数据库取到数据进行返回 
}void CacheMgr() // 缓存
{srand(time(0));if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}SQLMgr(); // 2.缓存出错抛异常,没出错调用数据库
}void HttpServer() // 网络
{// 模拟服务出错srand(time(0));if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}CacheMgr(); // 1.网络出错抛异常,没出错调用缓存
}int main()
{while (1){this_thread::sleep_for(chrono::seconds(1));try {HttpServer();}//catch (const HttpServerException& e) // 当子类和父类同时出现,按顺序走,优先匹配位置近的//{//	cout << "子类";//	cout << e.what() << endl;//}catch (const Exception& e) // 这里捕获父类对象就可以{// 通过多态拿错误信息.多态指向子类,通过调用子类拿到错误信息 cout << e.what() << endl; // e是父类对象,抛的可能是父类,调的就是父类的what.抛的如果是sql,指向的是子类,调what调用的则是子类的what}catch (...){cout << "Unkown Exception" << endl; // 捕捉未知异常}}return 0;
}

3.1 异常的重新抛出

有可能单个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]; // 如果抛异常会发生内存泄漏int len, time;cin >> len >> time;try // 所以捕获异常{cout << Division(len, time) << endl;}/*catch (const char* errmsg){cout << errmsg << endl;}*///catch (const char* errmsg)//{//	cout << "delete []" << array << endl; //	delete[] array;//	throw errmsg; // 重新抛出异常,抛之前释放一下//}catch (...) // 各种抛错异常,需要不同的捕获方法,用...便可一劳永逸{cout << "delete []" << array << endl; delete[] array;throw; // 异常的重新抛出,捕到什么抛什么}cout << "delete []" << array << endl; // 如果期望在main函数执行异常,且能够继续完成释放,这是就可以用异常的重新抛出delete[] array;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

需要注意的是:
构造函数完成对象的构造和初始化
不要在构造函数中抛出异常
否则可能导致对象不完整
或没有完全初始化

析构函数主要完成资源的清理
不要在析构函数内抛出异常
否则可能导致资源泄漏

C++中异常经常导致资源泄漏的问题
比如:
在new和delete中抛出异常导致内存泄漏
在lock和unlock之间抛出异常导致死锁
C++经常使用RAII来解决以上问题
而RAII在下一篇博客智能指针
会进行专门讲解

在这里插入图片描述
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见

相关文章:

【C++进阶9】异常

一、C语言传统的处理错误的方式 终止程序&#xff0c;如assert 如发生内存错误&#xff0c;除0错误时就会终止程序返回错误码 需要程序员自己去查找对应的错误 z如系统的很多库的接口函数都是通 过把错误码放到errno中&#xff0c;表示错误 二、C异常概念 异常&#xff1a;函…...

RecyclerVIew->加速再减速的RecyclerVIew平滑对齐工具类SnapHelper

XML文件 ItemView的XML文件R.layout.shape_item_view <?xml version"1.0" encoding"utf-8"?> <FrameLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"100dp"android:layout_heig…...

突破SaaS产品运营困境:多渠道运营如何集中管理?

随着数字化时代的到来&#xff0c;SaaS&#xff08;软件即服务&#xff09;产品已成为企业日常运营不可或缺的工具。然而&#xff0c;在竞争激烈的市场环境下&#xff0c;SaaS产品运营越来越重视多渠道、多平台布局&#xff0c;以更广泛地触及潜在用户&#xff0c;然而&#xf…...

智能语音热水器:置入NRK3301离线语音识别ic 迈向智能家居新时代

一、热水器语音识别芯片开发背景 在科技的今天&#xff0c;人们对于生活品质的追求已不仅仅满足于基本的物质需求&#xff0c;更渴望通过智能技术让生活变得更加便捷、舒适。热水器作为家庭生活中不可或缺的一部分&#xff0c;其智能化转型势在必行。 在传统热水器使用中&#…...

Redis集群部署合集

目录 一. 原理简述 二. 集群配置​​​​​​​ 2.1 环境准备 2.2 编译安装一个redis 2.3 创建集群 2.4 写入数据测试 实验一&#xff1a; 实验二&#xff1a; 实验三&#xff1a; 实验四&#xff1a; 添加节点 自动分配槽位 提升节点为master&#xff1a; 实验…...

【HDFS】关于Hadoop的IPC.Client类的一些整理

org.apache.hadoop.ipc.Client 类是IPC服务的一个客户端。 IPC请求把一个Writable对象当做参数,返回一个Writable对象当做结果value。 一个IPC服务运行在某个端口上,并且由参数class和value class定义。 Router里的IPC.Client对象就两个 有这样一个类:ClientCache 看名字就…...

Swoole v6 能否让 PHP 再次伟大?

现状 传统的 PHP-FPM 也是多进程模型的的运行方式&#xff0c;但每个进程只能处理完当前请求&#xff0c;才能接收下一个请求。而且对于 PHP 脚本来说&#xff0c;只是接收请求和响应请求&#xff0c;并不参与网络通信。对数据库资源的操作&#xff0c;也是一次请求一次有效&am…...

C++ STL Iterator Adapter

1. std::back_insert_iterator 使用 // back_insert_iterator example #include <iostream> // std::cout #include <iterator> // std::back_insert_iterator #include <vector> // std::vector #include <algorithm> // std::copy…...

android-aidl5

aidl类是实现Manager和Service通信的桥梁。 例如在修改Android Wifi功能的时候看到WifiManager管理WifiService&#xff1b; AIDL是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。 比如onclick&#xff08;&#xff09;&#xff0c;用oneway修…...

day01-项目介绍及初始化-登录页

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 day01-项目介绍及初始化-登录页一、人力资源项目介绍1.1项目架构和解决方案主要模块解决的问题 二、拉取项目基础代码1.引入库2.升级core-js版本到3.25.5按照完整依…...

华为开发者大会:全场景智能操作系统HarmonyOS NEXT

文章目录 一、全场景智能操作系统 - HarmonyOS NEXT1.1 系统特性1.2 关于架构、体验和生态 二、应用案例2.1 蚂蚁mpaas平台的性能表现 三、新版本应用框架发布3.1 新语言发布3.2 新数据库发布3.3 新版本编译器的发布 四、CodeArts和DataArts4.1 CodeArts4.2 DataArts 五、总结 …...

深度学习二分类评估详细解析与代码实战

深度学习二分类的实战代码&#xff1a;使用 Trainer API 微调模型. https://huggingface.co/learn/nlp-course/zh-CN/chapter3/3 如果你刚接触 自然语言处理&#xff0c;huggingface 是你绕不过去的坎。但是目前它已经被墙了&#xff0c;相信读者的实力&#xff0c;自行解决吧。…...

c++笔记容器详细介绍

C标准库提供了多种容器来存储和管理数据。这些容器属于<vector>, <list>, <deque>, <map>, <set>, <unordered_map>, <unordered_set>等头文件中。这些容器各有优缺点&#xff0c;适用于不同的场景。下面详细介绍几种主要的容器及其…...

CS144 Lab3 TCPSender复盘

一.基础概念 1.TCPSender在TCPSocket中的地位与作用 Lab0中实现了基于内存模拟的流控制-字节流&#xff08;ByteStream&#xff09;&#xff0c;底层使用std::deque实现&#xff0c;根据最大容量Capacity进行容量控制。个人理解它相当于应用层的输入输出缓存区&#xff0c;用户…...

建筑可视化中使用云渲染的几大理由

在建筑行业中&#xff0c;可视化技术已成为不可或缺的一部分。无论是设计方案的展示、施工进度的模拟&#xff0c;还是最终效果的呈现&#xff0c;建筑可视化都发挥着至关重要的作用。 建筑可视化是指通过计算机技术和图形学算法&#xff0c;将建筑设计、规划和施工过程中的数据…...

Python数据可视化-地图可视化

1.首先绘制实现数据可视化的思维导图 具体要实现什么功能-怎么处理&#xff0c;先把思路写好 数据来源&#xff1a; 爬取的数据 运行结果&#xff1a; 部分代码&#xff1a; 完整代码请在下方↓↓↓&#x1f447;获取 转载请注明出处&#xff01;...

leetcode 动态规划(基础版)单词拆分

题目&#xff1a; 题解&#xff1a; 一种可行的dp做法是基于完全背包问题&#xff0c;将s看成是一个背包&#xff0c;wordDict看作是物品&#xff0c;然后往s中放入物品判断最终是否可以变为给定的s即可。这道题和上一题都用到了在dp如何枚举连续子串和状态表示&#xff1a;枚…...

Ubuntu/Linux调试安装南京来可CAN卡

准备好USB rules文件和can driver文件备用! 必做&#xff1a;放置USB rules文件到对应位置处理权限问题 而后&#xff1a;安装内核driver并编译。需求众多依赖编译环境&#xff0c;视情况安装填补。如GCC,G,make等等 进入对应64bit文件夹中&#xff0c;添加权限&#xff0c;执…...

vue2+TS获取到数据后自动叫号写法

1.父组件写法 初始化&#xff1a; //引入子组件 <odialog ref"odialogRef" onSure"onSurea"></odialog> //子传父private onSurea() {// 初始化信息/重新叫号来的数据this.initTabelData()setTimeout(() > {// 播放声音的数据this.search…...

28、架构-边界:微服务的粒度

微服务的粒度 在设计微服务架构时&#xff0c;确定微服务的粒度是一个关键问题。粒度过大或过小都会带来不同的问题&#xff0c;因此需要找到合理的粒度来划分微服务。下面详细探讨微服务粒度的合理范围及其影响因素。 1. 微服务粒度的上下界 微服务的粒度不应该只有唯一正确…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...