【C++航海王:追寻罗杰的编程之路】异常——错误处理方式之一

目录

引言

1 -> C语言传统的处理错误的方式

2 -> C++异常概念

3 -> 异常的使用

3.1 -> 异常的抛出和捕获

3.2 -> 异常的重新抛出

3.3 -> 异常规范

4 -> 自定义异常体系

5 -> C++标准库的异常体系

6 -> 异常的优缺点


引言

在C++编程中,异常处理是一种重要的技术,用于处理程序在运行时可能出现的错误或意外情况。异常是指在程序执行过程中发生的某种不正常的情况,例如除以零、内存访问错误或无效的输入等。传统的错误处理方式通常涉及使用错误代码或返回特殊值来指示问题,但这种方式可能会导致代码混乱、繁琐,并且容易被忽略或处理不当。

异常处理提供了一种更为结构化和灵活的方法来处理异常情况。当异常发生时,程序可以抛出(throw)一个异常对象,然后在适当的位置捕获(catch)并处理该异常。这种机制使得程序可以将错误处理逻辑与正常逻辑分离开来,提高了代码的可读性和可维护性

1 -> C语言传统的处理错误的方式

传统的错误处理机制:

  1. 终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除以零错误时就会终止程序。
  2. 返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到error中,表示错误。

实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

2 -> C++异常概念

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接调用者处理这个错误。

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

如果有一个块抛出一个异常,捕获异常的方法会使用trycatch关键字。try块中放置可能抛出异常的代码,try块中的代码被称为保护代码。使用try/catch语句的语法如下:

try
{// 保护的标识代码
}
catch (ExceptionName e1)
{// catch 块
}
catch (ExceptionName e2)
{// catch 块
}
catch (ExceptionName e3)
{// catch 块
}

3 -> 异常的使用

3.1 -> 异常的抛出和捕获

异常的抛出和匹配原则

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

在函数调用链中异常栈展开匹配原则

  1. 首先检查throw本身是否在try块内部,如果是在查找匹配的catch语句。如果有匹配,则调到catch的地方进行处理。
  2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
  3. 如果达到main函数的栈,依旧没有匹配的,则终止程序。这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们都要在最后加一个catch()捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
  4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
#define  _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;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* errmsg){cout << errmsg << endl;}catch (...) {cout << "unkown exception" << endl;}return 0;
}

3.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;
}

3.3 -> 异常规范

  1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。可以在函数的后面接throw(类型),列出这个函数可能抛出的所有异常类型。
  2. 函数的后面接throw(),表示函数不抛异常。
  3. 若无异常接口声明,则此函数可以抛出任何类型的异常。

4 -> 自定义异常体系

实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法使用,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。

// 服务器开发中通常使用的异常继承体系
class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}protected:string _errmsg;int _id;
};class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}virtual string what() const{string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}private:const string _sql;
};class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};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 SQLMgr()
{srand(time(0));if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}//throw "xxxxxx";
}void CacheMgr()
{srand(time(0));if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}SQLMgr();
}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){this_thread::sleep_for(chrono::seconds(1));try {HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}

5 -> C++标准库的异常体系

C++提供了一系列标准的异常,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组。

异常描述
std::exception该异常是所有标准C++异常的父类
std::bad_alloc该异常可以通过new抛出
std::bad_cast该异常可以通过dynamic_cast抛出
std::bad_exception处理C++程序中无法预测的异常时非常有用
std::bad_typeid该异常可以通过typeid抛出
std::logic_error理论上可以通过读取代码来检测到的异常
std::domain_error当使用了一个无效的数字域时,会抛出该异常
std::invalid_argument当使用了无效参数时,会抛出该异常
std::length_error当创建了太长的std::string时,会抛出该异常
std::out_of_range该异常可以通过方法抛出,例如std::vector和std::bitset<>::operator[]()
std::runtime_error理论上不可以通过读取代码来检测到的异常
std::overflow_error当发生数学上溢时,会抛出该异常
std::range_error当尝试存储超出范围的值时,会抛出该异常
std::underflow_error当发生数学下溢时,会抛出该异常

说明:实际中我们可以去继承exception类实现自己的异常类。但是实际中很多公司像上面一样自己定义一套异常继承体系。因为C++标准库设计的不够好用。

int main()
{try{vector<int> v(10, 5);// 这里如果系统内存不够也会抛异常v.reserve(1000000000);// 这里越界会抛异常v.at(10) = 100;}catch (const exception& e) // 这里捕获父类对象就可以{cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}return 0;
}

6 -> 异常的优缺点

C++异常的优点:

  1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug
  2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们要层层返回错误,最外层才能拿到错误。
  3. 很多的第三方库都包含异常,比如boost、gtest、gmock等常用的库,那么我们使用它们也需要使用异常。
  4. 部分函数使用异常更好处理,比如构造函数没用返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

C++异常的缺点:

  1. 异常会导致程序的执行流乱跳,并且非常混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。
  2. 异常会有一些性能的开销。
  3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高。
  4. C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常混乱。
  5. 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用func() throw();的方式规范化。

感谢各位大佬支持!!!

互三啦!!!

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

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

相关文章

冯喜运:4.16中东对抗风暴下,黄金原油市场何去何从?

黄金行情走势分析&#xff1a;4小时图布林道开始收口&#xff0c;昨日下探至下轨附近&#xff0c;也是此前的起涨低点2320启稳上升&#xff0c;十字K线配合单阳拉起&#xff0c;重新去摸高上轨。目前4小时图处于摸高当中。周线和日线留意多空转换&#xff0c;摸高之后是强势延续…

Codeforces Round 926 (Div. 2) ---- E. Sasha and the Happy Tree Cutting ----题解

E. Sasha and the Happy Tree Cutting&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 现在有一颗树&#xff0c;然后给出了k对路径&#xff0c;然后要求路径上至少有一个结点是被染色了的&#xff0c;如果这k对路径没有共用边&#xff0c;那我们至少需要染色k条边。…

Docker安装及开启远程访问

这几天有人问我docker是怎么开启远程服务的&#xff1f; 正好之前我做过这件事情&#xff0c;并且写了相关的笔记&#xff0c;现在整理为一篇博客发出来。 安装Docker 首先更新一下自己的yum版本 yum update安装一下所需要的软件包 yum install -y yum-utils device-mappe…

MySQL进阶-----limit、count、update优化

目录 前言 一、limit优化 1. 未优化案例 2.优化后案例 二、count优化 count用法 三、update优化 1.锁行情况&#xff08;有索引&#xff09; 2.锁表情况&#xff08;无索引&#xff09; 前言 上一期我们学习了order by优化和group by优化&#xff0c;本期我们就继续学习…

Pytorch-张量形状操作

&#x1f606;&#x1f606;&#x1f606;感谢大家的观看&#x1f606;&#x1f606; &#x1f339; reshape 函数 transpose 和 permute 函数 view 和 contigous 函数 squeeze 和 unsqueeze 函数 在搭建网络模型时&#xff0c;掌握对张量形状的操作是非常重要的&#xff…

AI大模型日报#0415:贾佳亚团队新作王炸、马斯克首款多模态大模型、ChatGPT to B

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。标题: 融合ChatGPTDALLE3&#xff0c;贾佳亚团队新作开源&#xff1a;识图推理生图一站解决 摘要: 贾佳亚团队推出了多模态模型Mini-Gem…

【VIC水文模型】模型原理简介

VIC水文模型原理 VIC水文模型概述土壤&#xff08;Soil&#xff09;积雪&#xff08;Snow&#xff09;动态湖和湿地模型动态湖&#xff08;Lake Model&#xff09;湿地模型&#xff08;Wetland Model&#xff09; 1 VIC模型陆面水文过程&#xff08;产流过程&#xff09;1.1 能…

PyQt5设计师QtDesigner控件箱控件介绍及讲解

QtDesigner Qt Designer 是一个由Qt官方提供的图形用户界面设计工具&#xff0c;旨在帮助开发者快速、方便地设计和布局Qt应用程序的用户界面。Qt Designer提供了一个直观的可视化界面&#xff0c;开发者可以通过拖放、设置属性等方式设计界面&#xff0c;而无需编写繁琐的布局…

007 springboot整合mybatis-plus 增删改查 ModelAndView jsp 分页

文章目录 MybatisplusConfig.javaReceiveAddressController.javaReceiveAddress.javaReceiveAddressMapper.javaReceiveAddressServiceImpl.javaIReceiveAddressService.javaServerResult.javaServletInitializer.javaSpringbootDemoApplication.javareceive_address.sqlReceiv…

【canvas】canvas基础使用(九):文本绘制

简言 canvas除了能够绘制图形外&#xff0c;也可以绘制文本。 绘制文本 fillText() 填充文本 CanvasRenderingContext2D 对象的方法 fillText() 是 Canvas 2D API 的一部分&#xff0c;它在指定的坐标上绘制文本字符串&#xff0c;并使用当前的 fillStyle 对其进行填充。存…

论文解读:FREE LUNCH FOR FEW-SHOT LEARNING: DISTRIBUTION CALIBRATION

文章汇总 问题 学习到的模型很容易因为只有少数训练样本形成的有偏分布而变得过拟合。 动机 我们假设特征表示中的每个维度都遵循高斯分布&#xff0c;因此分布的均值和方差可以借鉴类似类的均值和方差&#xff0c;这些类的统计量可以通过足够数量的样本得到更好的估计。 …

11.盛最多水的容器(Java,双指针)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同…

Vue - 5( 16000 字 Vue2 入门级教程)

一&#xff1a;Vue 初阶 1.1 组件自定义事件 在 Vue 中&#xff0c;组件间通过自定义事件进行通信是一种常见的模式。自定义事件允许子组件向父组件发送消息&#xff0c;也可以在组件内部进行事件的绑定、触发和解绑。让我们详细讲解这些知识点。 1.1.1 组件自定义事件 在 …

季节更迭 关爱不变 | 鲁南制药四季守护您的健康生活

春天&#xff0c;万物复苏的季节&#xff0c;一切都充满了生机和活力。在春日的阳光下&#xff0c;鲜花盛开&#xff0c;绿叶茂盛&#xff0c;鸟儿欢歌&#xff0c;蝴蝶翩翩起舞。我们的身体也需要特别的关爱和养护&#xff0c;保持健康和活力&#xff0c;更好地迎接每一次季节…

知识跟踪模型GraphKT

1 知识跟踪Knowledge Tracing的概念 知识跟踪可以用来解决自适应学习问题。如何通过与教学材料的在线互动来有效地跟踪学生的学习进展&#xff1f;知识跟踪可用于量化学生的知识状态&#xff0c;即对教材所涉及的技能掌握水平。用于评估和模拟学生随着时间推移对技能的认知掌握…

前端CSS讲义1

什么是 CSS? CSS 指层叠样式表 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中&#xff0c;是为了解决内容与表现分离的问题 外部样式表可以极大提高工作效率 外部样式表通常存储在 CSS 文件中 多个样式定义可层叠为一 样式对网页中元素…

AI数字人对话之RealChar框架源码解读

零.功能介绍 与虚拟角色(非形象)进行文本或语音会话 体验地址:RealChar. 代码库:GitHub - Shaunwei/RealChar: 🎙️🤖Create, Customize and Talk to your AI Character/Companion in Realtime (All in One Codebase!). Have a natural seamless conversation with AI…

姿态估计-人脸识别mesh-3d手势识别-3d目标检测-背景分割-人脸关键点

往期热门博客项目回顾&#xff1a;点击前往 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别-3d姿态识别 深度学习小白学习路线 AI健身教练-引体向上…

字节8年经验之谈 —— 聊一聊自动化测试为什么很难落地!

聊一聊自动化测试为什么很难落地 在软件开发和质量保障的领域&#xff0c;测试是确保软件质量的关键环节。自动化测试是一种利用脚本和工具自动执行测试用例的方法&#xff0c;可以提高测试效率、减少人工错误&#xff0c;并支持持续集成和交付。自动化测试作为提高测试效率和…

企业图纸管理软件,企业图纸管理软件有哪些推荐?

企业图纸管理软件是一种专门用于组织、存储、管理和共享企业图纸及相关文档的工具。这类软件可以帮助企业实现图纸的集中化、规范化管理&#xff0c;提高工作效率&#xff0c;降低管理成本。 企业图纸管理软件的核心功能通常包括以下几个方面&#xff1a; 集中化管理&#xff1…