C++:异常

文章目录

  • 传统的处理错误的方式
  • C++异常
  • C++异常的使用
    • 抛异常的举例
    • 异常的重新抛出
    • 异常规范
  • 自定义异常体系
  • C++标准库中的异常体系
  • 异常的优缺点

本篇总结的是C++中关于异常的内容

传统的处理错误的方式

C语言中,对于传统的错误方式有

  1. 终止程序:例如assert,但是问题在于有些过于暴力了,直接会终止程序
  2. 返回错误码:检查错误不太方便,需要去查找错误,成本比较高

C++异常

因此针对这种情况,C++新增了异常的概念,异常是一种处理错误的方式,当一个函数发现自己无法处理的错误的时候就可以抛出异常,让函数直接或间接的让调用者来处理这个错误

  1. throw:当问题出现的时候,会抛出异常,这是throw关键字来表示的
  2. catch:当有需要处理的问题时,catch用于捕获这个异常,可以有多个catch进行捕获
  3. trytry当中的代码标识将被激活的特定异常,在它的后面会跟着catch用于异常捕获

C++异常的使用

抛出和匹配原则

  1. 异常是抛出对象抛出的,因此这个对象的类型决定了要如何catch这个异常
  2. 被选中的处理代码是调用链中的与该对象匹配且离异常位置最近的那个
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在catch后销毁,有些类似于传值返回
  4. 实际中的抛出和捕获也有例外,比如抛出派生类对象,可以用基类捕获,多态的原理

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

  1. 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理
  2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
  3. 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止
  4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行

抛异常的举例

void div1(int x, int y)
{if (y == 0){throw "除0错误";}else{cout << x / (double)y << endl;}
}int main()
{int x = 0, y = 0;cin >> x >> y;try{div1(x, y);}catch(const char* str){cout << str << endl;}return 0;
}

异常的重新抛出

在实际的使用中,可能会遇到单个的catch不能处理一个异常,需要把这个异常再次进行抛出,所以catch是可以重新抛出给更上层的函数的

举个例子:比如下面的场景

void div1(int x, int y)
{int* p = new int[10];if (y == 0){throw "除0错误";}else{cout << x / (double)y << endl;delete[] p;cout << "delete[]" << endl;}
}

如果只是这样单纯的抛异常,那么对于p所指向的这段空间就得不到释放,在实际的开发中这种内存泄漏是十分严重的情景,因此处于这种原因,可以在重新抛出异常

double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{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. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些,可以在函数的后面加一个throw(类型),来说明这个函数可能抛出的所有异常类型
  2. 如果只是加一个throw,则表示函数不抛异常
  3. 若没有无异常接口声明,则此函数可以抛任何类型的异常

自定义异常体系

在实际的开发过程中,异常体系是需要被自定义出来的,因此会提前定义一套继承的规范体系,抛出的都是继承的派生类对象,只需要捕获一个基类就可以了

在这里插入图片描述

// 服务器开发中通常使用的异常继承体系
// 定义了一个异常基类
class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}// 可以在外部显示的查看错误信息是什么virtual string what() const{return _errmsg;}
protected:// 通常包括有异常信息和抛出异常所对应的id号码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;}
};// web服务器异常
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 = '张三'");}
}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;
}

这样就能把错误信息统一的放置到一个日志中,方便进行查看哪部分内容可能会出现错误

C++标准库中的异常体系

在这里插入图片描述
在这里插入图片描述

异常的优缺点

优点:

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

缺点:

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

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

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

相关文章

【STL】手撕 string类

目录 1&#xff0c;string类框架 2&#xff0c;string&#xff08;构造&#xff09; 3&#xff0c;~string&#xff08;析构&#xff09; 4&#xff0c;swap&#xff08;交换&#xff09; 5&#xff0c;string&#xff08;拷贝构造&#xff09; 1&#xff0c;常规法 2&a…

Cmkae外部依赖管理

文章目录 一、cmake依赖管理介绍二、源码依管理1. FetchContent与find_package进行集成 2. CPM3. git submodule附加&#xff1a; address_sanitizer 和 undefined sanitizer 一、cmake依赖管理介绍 CMake 是跨平台的构建系统&#xff0c;支持 C/C、Objective-C、Fortran 等多种…

Git Bash环境下用perl脚本获取uuid值

在Linux环境下&#xff0c;比如在ubuntu就直接有uuidgen命令直接获取uuid值。在Windows环境下常用的git bash中没有对应的命令&#xff0c;略有不便。这里用脚本写一个uuidgen&#xff0c;模拟Linux环境下的uuidgen命令。 #! /usr/bin/perl use v5.14; use Win32;sub uuidGen {…

6-13连接两个字符串

#include<stdio.h> int main(){int i0,j0;char s1[222],s2[333];printf("请输入第一个字符串&#xff1a;\n");gets(s1);//scanf("%s",s1);printf("请输入第二个字符串&#xff1a;\n");gets(s2);while(s1[i]!\0)i;while(s2[j]!\0)s1[i]s2…

1.1卷积的作用

上图解释了1∗1卷积如何适用于尺寸为H∗W∗D的输入层&#xff0c;滤波器大小为1∗1∗D&#xff0c;输出通道的尺寸为H∗W∗1。如果应用n个这样的滤波器&#xff0c;然后组合在一起&#xff0c;得到的输出层大小为H∗W∗n。 1.1∗1卷积的作用 调节通道数 由于 11 卷积并不会改…

App测试之App日志收集及adb常用命令

文章目录 前言一、adb是什么1.APP测试收集手机日志常用的工具2.adb下载与安装3.ADT/SDK/ADB是什么4.adb连接真机 二、adb常用命令三、android系统日志文件1.logcat日志文件2.logcat日志文件分析 四、分析crash & ANR 日志1.发生crash如何分析2.发生ANR如何分析 总结扩展&am…

前端组件库开发

通常我们会使用很多组件库&#xff0c;有时候我们会去看源码比如element&#xff0c;antd&#xff0c;然后发现多少是按需导出&#xff0c;和vue.use全局注册&#xff0c;依赖于框架的拓展。 组件库的开发依赖框架的版本和node的版本&#xff0c;这个是需要说明的&#xff0c;然…

【阅读论文】时间序列异常检测:综合评价

文章目录 摘要1 异常检测范围2 时间序列和异常3 异常检测技术3.1预测方法3.2重建方法3.3编码方法3.4距离方法3.5分布方法3.6隔离树方法 4 实验评估4.1环境和设置4.2结果质量4.3运行时和内存 5 讨论致谢参考文献 摘要 在金融应用中&#xff0c;检测时间序列数据中的异常子序列是…

ubuntu下QT搭建Android开发环境

一、前言 用QT开发android和直接使用android开发的区别 使用Qt开发Android应用和直接使用Android开发工具&#xff08;例如Android Studio&#xff09;有一些区别&#xff0c;主要体现在开发工具、语言、界面设计和性能等方面&#xff1a; 开发工具&#xff1a; Qt Creat…

Python----字典练习

相关链接&#xff1a;Python---字典的增、删、改、查操作_python中字典的增删改查-CSDN博客 Python---字典---dict-CSDN博客 Python---引用变量与可变、非可变类型-CSDN博客 重点&#xff1a; 字典中的 key &#xff08;就是键&#xff09;可以是很多数据类型&#xff08;…

Java线程池的使用和最佳实践

第1章&#xff1a;引言 处理并发问题时&#xff0c;如果每次都新建线程&#xff0c;那系统的压力得有多大&#xff1f;这时候&#xff0c;线程池就像一个英雄一样出现了&#xff0c;它帮我们有效地管理线程&#xff0c;提高资源利用率&#xff0c;降低开销。那么&#xff0c;为…

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理 &#xff08;一&#xff09;定义 1、定义 为其他对象提供一种代理以控制对这个对象的访问&#xff1b; 2、涉及到的角色 &#xff08;1&#xff09;抽象主题角色&#xff1a;真实主题和代理主题的共同接口&#xff0c;便于在使用真实主题的地方都可以使用代理…

鸿蒙开发笔记

最近比较火&#xff0c;本身也是做前端的&#xff0c;就抽空学习了下。对前端很友好 原视频地址&#xff1a;黑马b站鸿蒙OS视频 下载安装跟着视频或者文档就可以了。如果你电脑上安装的有node&#xff0c;但是开发工具显示你没安装&#xff0c;不用动咱们的node&#xff0c;直…

红队攻防实战之Access注入

若盛世将倾&#xff0c;深渊在侧&#xff0c;我辈当万死以赴 访问漏洞url: 1.Access联合查询 判断是否有注入 and 11正常&#xff0c;and 12出错 判断字段数 order by 7正常 order by 8出错 爆破出表名并判断回显点为2&#xff0c;5 查看字段内容&#xff0c;将字段名填入回…

12月1号作业

实现运算符重载 #include <iostream>using namespace std; class Person{friend const Person operator-(const Person &L,const Person &R);friend bool operator<(const Person &L,const Person &R);friend Person operator-(Person &L,const …

WakaTime一个用于跟踪和分析编程时间的工具

WakaTime是一个用于跟踪和分析编程时间的工具&#xff0c;它可以集成到各种代码编辑器和集成开发环境中&#xff0c;例如Visual Studio Code、Sublime Text、PyCharm等。它可以帮助开发人员了解他们花费在不同项目和编程语言上的时间&#xff0c;以及他们的编码习惯和生产力。 …

【面试HOT200】二叉树——广度优先搜索篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot200】进行的&#xff0c;每个知识点的修正和深入主要参…

SpringSecurity工作原理

实现功能就是继承这几个对应功能的类。 大概工作流程 Spring Security 的过滤器&#xff08;Filters&#xff09;和拦截器&#xff08;Interceptors&#xff09;是 Spring Security 框架中用于保护 web 应用安全的重要组件。它们在处理 HTTP 请求时扮演不同的角色&#xff0c…

uni-app一些目录结构、方法、生命周期、打包、微信小程序登录与支付

1、关于uniapp的目录结构 跟普通vue项目目录结构差不多&#xff0c;多了几个核心文件&#xff0c;manifest.json是配置应用名称、appid、logo、版本等打包信息用的&#xff0c;pages.json的作用是配置页面路径、页面窗口样式、tabBar、navigationBar等页面类信息 2、页面适配方…