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

【C++】C++11 异常

目录

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

2. C++异常概念

3. 异常的使用

3.1. 异常的抛出和捕获

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

3.3. 异常的重新抛出

3.4. 异常安全

3.5. 异常规范

4.自定义异常体系

5. C++标准库的异常体系

6. 异常的优缺点

6.1. C++异常的优点:

6.2. C++异常的缺点:


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

传统的错误处理机制:

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

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

2. C++异常概念

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

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

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

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

3. 异常的使用

3.1. 异常的抛出和捕获

异常的抛出和匹配原则

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
#include<iostream>
using namespace std;void func1()
{throw 1; // 这里抛出得对象是一个整型,所以会被捕获整型的catch块所捕获
}void func2()
{throw "error"; // 这里抛出得对象是一个字符串,所以会被捕获字符串的catch块所捕获
}int main()
{try {func1();//func2();}catch(int errid){cout << errid << endl;}catch (const char* str){cout << str << endl;}return 0;
}

而如果将func1注释,调用func2,就会被下面的catch块所捕获。

  1. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
void func1()
{throw 1;
}
void func2()
{try{func1();}catch (int errid) // func1抛出异常,由于在调用链中func2捕获的位置比main函数内捕获的位置更近,所以会匹配func2中的catch{cout << errid << endl;}}int main()
{try{func2();}catch (int errid){cout << errid << endl;}return 0;
}

  1. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
  2. catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
void func1()
{throw 1;
}int main()
{try{func1();}catch (...){cout << "未知异常" << endl;}return 0;
}

  1. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用,以后主要用到的也是这种方式

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

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

3.3. 异常的重新抛出

有可能单个的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) // 捕获Func重新抛出的异常{cout << errmsg << endl;}return 0;
}

3.4. 异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题。

3.5. 异常规范

  1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型。
  2. 函数的后面接throw(),表示函数不抛异常。
  3. 若无异常接口声明,则此函数可以抛掷任何类型的异常。
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator new (std::size_t size, void* ptr) throw();

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; // 异常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();
}void ServerStart()
{while (1){this_thread::sleep_for(chrono::seconds(1));try{HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}
}int main()
{ServerStart();return 0;
}

5. C++标准库的异常体系

C++ 提供了一系列标准的异常,定义在 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:

6. 异常的优缺点

6.1. C++异常的优点:

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

6.2. C++异常的缺点:

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

相关文章:

【C++】C++11 异常

目录 1. C语言传统的处理错误的方式 2. C异常概念 3. 异常的使用 3.1. 异常的抛出和捕获 3.2. 在函数调用链中异常栈展开匹配原则 3.3. 异常的重新抛出 3.4. 异常安全 3.5. 异常规范 4.自定义异常体系 5. C标准库的异常体系 6. 异常的优缺点 6.1. C异常的优点&…...

关于Thread.start()后的困惑、imap

在for循环中&#xff0c;接着开thread&#xff0c;开完就start&#xff0c;当时有个困惑&#xff0c;就是比如开的一个thread的这个start执行完&#xff0c;但是这个for循环还没执行完&#xff0c;那程序会跑到for循环的后面逻辑吗&#xff1f;比如下面13行for循环开始开第一个…...

qml学习之qwidget与qml结合使用并调用信号槽交互

学习qml系列之一说明&#xff1a; 学习qml系列之qwiget和qml信号槽的交互使用&#xff0c;并在qwidget中显示qml界面 在qml中发送信号到qwidget里 在qwidget里发送信号给qml 在qwidget里面调用qml界面方式 方式一&#xff1a;使用QQuickView 这个是Qt5.0中提供的一个类&…...

【 华为OD机试 2023】 组装新的数组(C++ Java JavaScript Python)

文章目录 题目描述输入描述输出描述备注用例题目解析C++JavaScriptJavaPython题目描述 给你一个整数M和数组N,N中的元素为连续整数,要求根据N中的元素组装成新的数组R,组装规则: R中元素总和加起来等于MR中的元素可以从N中重复选取R中的元素最多只能有1个不在N中,且比N中…...

【洛谷 P2089】烤鸡(循环枚举)

烤鸡 题目背景 猪猪 Hanke 得到了一只鸡。 题目描述 猪猪 Hanke 特别喜欢吃烤鸡&#xff08;本是同畜牲&#xff0c;相煎何太急&#xff01;&#xff09;Hanke 吃鸡很特别&#xff0c;为什么特别呢&#xff1f;因为他有 101010 种配料&#xff08;芥末、孜然等&#xff09;…...

windows10安装ubantu双系统

windows10安装ubantu双系统 文章目录windows10安装ubantu双系统一、安装前准备1.前期说明2.制作U盘启动器3.设置硬盘分区相关4.设置给ubantu系统的硬盘大小&#xff0c;设置为未分配&#xff08;删除卷&#xff09;二、进行安装1.设置bios相关2.进入bios启动界面选择U盘安装3.进…...

【华为OD机试 2023】 人数最多的站点/小火车最多人时所在园区站点(C++ Java JavaScript Python)

文章目录 题目描述输入描述输出描述用例题目解析C++JavaScriptJavaPython励志做全网最全、解法最多的华为OD机考算法题库,帮助你上岸华为。提供C++/Java、JavaScript、Python四种语言的解法。每篇文章都有详细的结题步骤。有问题,随时解答。😁😁😁😁 目前为了造福广大…...

2024届暑期实习实录(阿里云大数据研发平台)

1. 项目介绍&#xff08;介绍一下你觉得有挑战的项目 &#xff08;1&#xff09;项目的痛点需求&#xff08;配置变更的痛点、你做的目的是什么&#xff1f;&#xff09; 思考方向&#xff1a;业务背景&#xff0c;用户需求&#xff1b;产品发展&#xff0c;产品现有局限问题…...

空口协议probe req和probe rsp 、auth req和auth rsp 、assoc req和assoc rsp讲解

我们经常可以看到抓到的报文主要有三种:probe req和probe rsp 、auth req和auth rsp 、assoc req和assoc rsp 。 建立联结的三个阶段 相互发现阶段:probe req和probe rsp probe是探测的意思 相互了解阶段:auth req和auth rsp auth是认证的缩写 建立关…...

vscode ssh一直卡在wget的解决方案

vscode ssh一直卡在wget的解决方案找到commit_id 在服务器下点进该目录 .vscode-server\bin 一般日期最新的那一串就是我们需要的commit_id下载vscode-server-linux-x64.tar https://update.code.visualstudio.com/commit:${commit_id}/server-linux-x64/stable 将加粗部分替换…...

【Python学习笔记】第二十五节 Python MySQL

Python 连接到 MySQL 数据库有几种不同的连接方法&#xff0c;而且不是所有的方法都能与不同的操作系统很好地配合.MySQL connector/Python模块是Oracle支持的官方驱动&#xff0c;用于通过Python连接MySQL。该连接器完全是Python语言&#xff0c;而mysqlclient是用C语言编写的…...

折叠屏手机自带的屏幕表面层为什么不能自己撕?

计划入手折叠屏手机的小伙伴看过来&#xff0c;因为折叠屏手机外观的特殊形态&#xff0c;在日常使用中与普通直板手机屏幕的养护还是有着很大的不同&#xff0c;比如直板机入手后新机开箱撕膜这个操作&#xff0c;对于折叠屏手机来说&#xff0c;万万不可&#xff01;除此之外…...

20.hadoop系列之Yarn资源调度器

Yarn是一个资源调度平台&#xff0c;负责为运算程序提供服务器运算资源&#xff0c;相当于一个分布式的操作系统&#xff0c;而MapReduce等运算程序则相当于运行于操作系统之上的应用程序 1.Yarn基础架构 Yarn主要由ResourceManager、NodeManager、ApplicationMaster和Contai…...

206页16万字城市运行“一网统管”体系建设项目需求报告

本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 第 一 章 应用场景示例 一.1 一卡通人员管理针对企业厂区、办公楼等场所人员出入频繁、安保问题多样化、管理环节复杂等现状&#…...

【JS】数组Array的使用

arr.length 3 通过设置数组长度&#xff0c;实现截取数组&#xff0c;改变原数组 map 循环遍历数组&#xff0c;不改变原数组&#xff0c;返回一个新的数组&#xff08;可用变量接收&#xff09; forEach 循环遍历数组&#xff0c;不改变原数组 join 把数组转化为字符串&…...

2023年,软件测试怎么样?

2022年因为各种不可抗力原因&#xff0c;大厂裁员&#xff0c;失业等等频频受到关注。 不解释&#xff0c;确实存在&#xff0c;各行各业都很难&#xff0c;但是&#xff0c;说软件测试行业不吃香&#xff0c;我还真不认同&#xff08;不是为培训机构说好话&#xff0c;大环境…...

【学习笔记】NOMURA Programming Competition 2020

C - Folia 不难想到自底向上确定树的形态。可能要多尝试一下 一开始想错了好几个地方&#xff0c;服了 假设某一层有XXX个节点&#xff0c;那么上一层可能有⌈X2⌉,⌈X2⌉1,...,X\lceil\frac{X}{2}\rceil,\lceil\frac{X}{2}\rceil1,...,X⌈2X​⌉,⌈2X​⌉1,...,X个节点&…...

iis下常用程序的伪静态规则列表(包括wordpress、thinkphp)

shopex discuz2.0 discuz2.5 discuz3.x 淘宝客 ecshop phpwind参照http://www.west.cn/faq/list.asp?unid797通过主机面板设置即可 wordpress设置&#xff1a; 第一步&#xff1a; 1.新建一个“chineseurl.php”文件&#xff1a;在里面写入以下代码上传到wordpress安装目录。…...

【Python语言基础】——Python Select From

Python语言基础——Python Select From 文章目录 Python语言基础——Python Select From一、Python Select From一、Python Select From 从表中选取 如需从 MySQL 中的表中进行选择,请使用 “SELECT” 语句: 实例 从表 “customers” 中选取所有记录,并显示结果: import m…...

数据增广真有那么神奇吗?

作者&#xff1a;皮皮雷 来源&#xff1a;投稿 编辑&#xff1a;学姐 论文题目 How Effective is Task-Agnostic Data Augmentation for Pretrained Transformers? 论文作者 S. Longpre, Y. Wang, and C. DuBois 论文发表于 2020 EMNLP findings 摘要 任务无关的数据增广…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

Windows 下端口占用排查与释放全攻略

Windows 下端口占用排查与释放全攻略​ 在开发和运维过程中&#xff0c;经常会遇到端口被占用的问题&#xff08;如 8080、3306 等常用端口&#xff09;。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口&#xff0c;帮助你高效解决此类问题。​ 一、准…...