C++ day34 异常(三)异常规范,未捕获异常,意外异常,异常导致内存泄漏

2023-05-16

文章目录

  • 异常规范(C++98添加,C++11已摒弃,不建议使用)
    • 异常规范的作用(正是这俩作用的鸡肋和难办使它失去了粉丝)
    • C++11支持的特殊的异常规范:关键字noexcept(程序员的庄严承诺)
  • 异常被引发后可能出现的两种问题
    • 未捕获异常 uncaught exception
      • 示例
    • 意外异常 unexpected exception(C++11摈弃异常规范的另一理由)
  • 异常处理可能导致==内存泄漏==:解决办法是智能指针模板
  • 总结

异常规范(C++98添加,C++11已摒弃,不建议使用)

exception specification

由于大量已用代码用了异常规范,所以暂未从标准中完全剔除,但是完全去掉指日可待。

其实我之前看到它已经被摒弃就不打算深入了解他了,但后面好多代码都涉及到这个东西。。。。搞的我很被动。只好回头来学习。

下面代码中的throw部分就是异常规范,它后面的圆括号里面是类型列表,但是也可以不包含类型列表。

double harm(double a) throw(bad_thing);//表示也许harm函数会抛出bad_thing异常
double harm(double a) throw();//表示harm函数不会抛出异常

异常规范可能出现的位置:

  • 函数原型
  • 函数定义

异常规范的作用(正是这俩作用的鸡肋和难办使它失去了粉丝)

  • 告诉程序员可能需要使用try块

给函数指定异常规范,以让函数的用户知道要捕获哪些异常

即如果函数的原型和函数定义中有异常规范,则调用该函数时最好用try块把它包起来并写catch块来处理异常。

比如:

double argh(double, double) throw(out_of_bounds);//函数原型,指明了可能会引发out_of_bounds异常

则使用时就这么用:

try{
	x = argh(y, z);
}
catch{
	···
}

但是C++11并不买账,它认为可能直接在函数原型旁边写注释提醒程序员用try块,干嘛非要加个语法工具呢,明明注释就可以告诉大家要用try块了。

  • 让编译器添加执行运行阶段检查的代码,即检查是否违反了异常规范。

但是编译器表示我好难。。因为很难检查是不是引发了异常,比如harm函数没有引发,但是harm调用的另一个函数引发了;或者以前没引发,更新一个库以后就引发了。。。

原则上,异常规范的规范列表列表应该包含此函数调用的其他函数引发的异常。比如,a函数调用了b函数,b函数可能引发retort对象异常,那么a函数和b函数的异常规范中都应该包含retort对象异常。

可是,a函数怎么知道自己内部具体调用了多少函数,调用的这些函数又调用了哪些函数,无底洞啊简直,就算编译器不辞辛劳,真的找到了a函数会直接和间接调用的所有函数,那把他们可能引发的异常都写进大家的异常规范里??多长啊得,又不好维护。。。编译器表示头大。写编译器的人更头大。

总之,编译器和为编译器写这种检查代码的程序员发现,这个活真的不好干,于是大家达成一致意见:不要用异常规范了。C++11也觉得很有道理,就建议大家不要使用这个看似很有前景,看似会对代码安全有贡献的特性了。

C++11支持的特殊的异常规范:关键字noexcept(程序员的庄严承诺)

这个关键字是C++11新增的。用来表示函数不会引发异常,这其实是程序员的一份庄严承诺。程序员必须足够自信,才能信誓旦旦的使用该关键字。

使用它的目的和好处:有助于编译器优化代码,编译器知道这个函数一定不会引发异常,就无需对它多余关照了。

异常被引发后可能出现的两种问题

这两种情况,默认都会导致程序异常终止

未捕获异常 uncaught exception

如果异常不是在带有异常规范的函数中引发,即引发这个异常的函数并没有写异常规范,那么必须要捕获他

如果不捕获,即没写try块把引发异常的代码包起来,或者没找到匹配类型的catch块,则这种异常就叫未捕获异常。

未捕获异常不会导致程序立刻终止。程序会首先调用头文件exception中的terminate()函数,terminate()函数默认调用abort()函数来终止程序。但是,我们可以用exception头文件中的set_terminate()函数来修改terminate函数到底调用谁,即可以让他不调用abort(),而是调用比如exit()函数。

下面是exception头文件中set_terminate()函数和terminate()函数的声明:

typedef void (*terminate_handler)();//terminate_handler是一个函数指针类型,指向没有参数也没有返回值的函数
terminate_handler set_terminate(terminate_handler f) noexcept;
void terminate() noexcept;

函数指针,即函数的地址,难道matlab里的函数句柄其实就是函数的地址?记得学的时候书上就解释说是一种间接的使用函数的方式啦巴拉巴拉的,直接说是函数的地址不就好了??不就一下理解了???网上那么多人都觉得matlab的函数句柄略难理解,难道大家没有一个学C的函数指针的?
看到一个解释通透的啦,看来句柄确实是地址。在这里插入图片描述

如果多次调用set_terminate()函数,则terminate函数对调用最后一次set_terminate函数设置的函数。

#include <exception>
using namespace std;
void myQuit()
{
	cout << "Terminating due to uncaught exception\n";
	exit(5);
}
//在自己主程序的开头,指定终止操作调用自己写的终止函数
set_terminate(myQuit);//myQuit函数名其实就是一个函数地址,一个函数指针

示例

自己写了一个未捕获异常的示例,即new请求内存失败,我没用try块把new语句包起来,引发的异常就是未捕获的异常。

嘻嘻

using namespace std;
void myQuit()
{
	cout << "Terminating due to uncaught exception\n";
	exit(5);
}
int main()
{
	set_terminate(myQuit);
    double * d = new double[100000000000000];//引发异常后,程序先调用terminate,然后调用myQuit

	return 0;
}
Terminating due to uncaught exception

Process returned 5 (0x5)   execution time : 0.508 s
Press any key to continue.

意外异常 unexpected exception(C++11摈弃异常规范的另一理由)

没有和异常规范的规范列表中的某种异常匹配,则是意料之外的异常,即函数引发了其异常规范中没有的异常

异常规范里写到的异常类型是预期的异常。

由于定义中涉及到异常规范,所以这种异常必须由带异常规范的函数来引发

异常规范:C++11不建议使用,但还没被踢出标准。

这种情况下,程序会调用unexpected()函数,这个函数再去调用terminate函数,然后terminate还是默认调用abort函数来终止程序。

但是unexpected函数的行为也是可以修改的,用一个叫做set_unexpected的函数来修改。这几个函数在exception头文件的声明如下:

typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler f) noexcept;//把函数指针作为参数传入
void unexpected() noexcept;

天哪,我不知道下面这段在说什么,怎么这么复杂,我服了,溜了
在这里插入图片描述

double argh(double x, double y) throw(out_of_bounds, bad_exception);
try{
	x = argh(a, b);
}
catch(out_of_bounds * ex){
	···
}
catch(bad_exception & ex){
	···
}

异常处理可能导致内存泄漏:解决办法是智能指针模板

当异常处理遇上动态内存分配,就有可能会出事儿。

先看一个不会出事儿的:

void test1(int n)
{
	string mesg("I'm trapped in an endless loop");
	...
	if (oh_no)
		throw exception();
	···
	return;
}

如果oh_no为true,throw语句会提前终止函数,但是动态分配内存的mesg对象由于栈解退,还是成功的被调用了析构函数,即动态分配的内存会被释放,就不会造成内存泄漏。

所以这里没出问题,主要是因为mesg是一个类对象,有析构函数,所以在析构函数里释放了动态分配的这些内存。

string类的构造函数使用了动态内存分配哦!!!

再看看会出事的:

void test2(int n)
{
	double * d = new double[n];
	···
	if (oh_no)
		throw exception();
	···
	delete [] d;
	return;
}

这里就不一样了,double指针d,不是对象,没有析构函数的加持,于是throw以后,函数终止,后面的delete就执行不了了,栈解退只能把变量d,即double指针变量,释放,并不能释放d指向的那一堆内存啊。于是这对内存就泄露了,因为指向他们的指针都没了。

所以还是来自类这种大家族比较好,有背景就是不一样,人家的析构函数在栈解退时会被自动调用就把内存释放了。而来自基本数据类型的,比如int,double等的对象,就没有析构函数这种外挂工具,一旦出现大的风雨比如异常,就玩去哪没有招架之法,只能眼睁睁看着自己指向的内存被泄露。

那上面这种情况要怎么处理呢?办法总是有的。只要智商不滑坡,办法总比困难多。

void test2(int n)
{
	double * d = new double[n];
	···
	try{
		if (oh_no)
			throw exception();
		}
		catch(exception & ex){//处理方法:先捕捉异常,然后只是释放内存,再帮你重新引发一个一样的异常
		delete [] d;
		throw;
	}
	···
	delete [] d;
	return;
}

这种处理很聪明,我先捕获你,但其实我并不会真的帮你处理你的问题,而只是想利用你解决我的问题,我把我的内存释放完了,我再帮你引发一个同样的异常,也不耽误你的事儿,还把我的危机化解了。聪明。

但是毕竟你想不到一切可能发生的异常情况,所以这种临时补墙刷灰的操作肯定不完美,也许还会背的疏忽和错误呢?所以这种情况下,其实最好的办法是智能指针模板

总结

  • 函数可以包含异常规范,以指出自己可能引发的各种异常,但是C++11不建议大家使用。

  • 未捕获异常(没有匹配的catch块)默认情况下会终止程序。

  • 意外异常(没有和任何异常规范匹配的异常)默认情况下也会终止程序。

  • 应该在设计程序时候就加入异常处理的功能,不是以后再加。

  • 使用异常,虽然会提升代码的安全性,但是也会增加代码长度,还会降低程序运行速度。任何工具都是双刃剑,按需使用。

对于某些重要的项目,异常处理是非常重要的,不进行异常错误处理的代价也许更高,虽然会增加代码工作量和程序时间,但是也要做。

  • 异常规范不可以用于模板,因为函数模板经过不同的具体化方式,引发的异常也不尽相同。
  • 异常和动态内存分配也许会冲突。

大体意思就是:现在学了这么多,仍然只是皮毛,建立起来一个要处理异常的概念,但是怎么处理,这个实现操作却需要自己去看库文档和类源码的异常例程,自己去总结和学习异常处理的细节。这只是万里长征第一步。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ day34 异常(三)异常规范,未捕获异常,意外异常,异常导致内存泄漏 的相关文章

  • STM32开发 数据包环形队列

    目录 前言一 构建环形队列结构体二 队列初始化三 读写数据1 队列满判断2 队列空判断3 队列写入数据4 队列读取数据 四 实际使用 前言 环形队列的理论知识网上有很多文章 xff0c 这里我就仅通过代码分享一下使用经验 xff0c 在我的
  • 静态链接库lib和动态链接库ddl的区别和联系

    静态链接库lib和动态链接库ddl的区别 联系 xff1a 都是在链接阶段使用的 区别 xff1a 不同的是静态链接库中的代码会直接放到exe中 xff0c 而动态链接库在使用时才会被加载到这个exe执行的内存空间 xff0c 所以使用静态
  • 单片机与上位机的串行通信

    写在前面 这篇博客主要记录下单片机是如何通过TXD RXD与上位机进行数据交换的 先介绍下51单片机中与串口通信有关的各种寄存器 首先 xff0c 上位机如果要发送数据给单片机 xff0c 单片机接收到数据之后 xff0c 会存入到SBUF
  • 【C++知识】关于迭代器失效的几种情况

    前言 关于面试时有被问到过这类问题 xff0c 当时由于只一知半解 xff0c 回答的不是特别好 xff0c 所以今天自己特意来实验一下 希望能帮助大家有同样疑惑的人解答疑惑 xff01 目录 关于迭代器失效的几种情况 1 序列式容器迭代器
  • Yolov3+C+++opencv+VS2015成功检测

    nbsp 前言 nbsp nbsp nbsp 最近在用yolov3进行目标检测 也有一个多星期了 想把最近做出的一些成果记录下来 供大家参考下 我的运行环境是C opencv VS2015 yolov3 下面将简单介绍下yolo的一些思想
  • simulink之S函数

    s函数是system Function的简称 xff0c 用它来写自己的simulink模块 xff08 够简单吧 xff0c xff0c 详细的概念介绍大伙看帮助吧 xff09 可以用matlab C C 43 43 Fortran Ad
  • win10解决未安装任何音频输出设备

    最近刚刚更新了一下win10系统 xff0c 开始啥问题没有 xff0c 晚上睡觉关机后 xff0c 第二天开机 xff0c 小喇叭处有一个红叉 xff0c 显示未安装任何音频输出设备 查看了微软的官网以及百度了很多解决方法 xff0c 电
  • Quadcopter控制

    1 问题描述 四旋翼飞行器对角线上的两个电机旋转方向相同 xff0c 另一对与之旋转方向相反 这是使推力 xff0c 滚转 xff0c 俯仰 xff0c 偏航相互独立控制的必要条件 这可以使我们命令其中的一个动作而不影响其他动作 实际上 x
  • 入门级都能看懂的softmax详解

    项目github地址 xff1a bitcarmanlee easy algorithm interview and practice 经常有同学私信或留言询问相关问题 xff0c V号bitcarmanlee github上star的同学
  • LQR制导

    LQR制导 引言 在ardupilot中固定翼飞机横航向位置控制 xff08 制导律 xff09 采用L1制导律 xff0c 最近想将一些其他的控制理论用于ardupilot代码中 xff0c 通过ardupilot论坛 xff0c 看到已
  • 2022年度总结

    年度总结 参加工作的第一年很快就过去了 xff0c 从四月份离校到公司 xff0c 直到农历腊月27回家 xff0c 工作了9个月的时间 xff0c 总的来说工作和学习的差别还是很大的 xff0c 从学生到社畜的转换还是花了一段时间的 接下
  • HTTP基本认证

    在HTTP中 xff0c 基本认证 xff08 英语 xff1a Basic access authentication xff09 是允许http用户代理 xff08 如 xff1a 网页浏览器 xff09 在请求时 xff0c 提供 用
  • c# 设置代理服务器发送http请求

    span class token keyword using span span class token namespace System span span class token punctuation span span class
  • Blaze:高性能C++数学库

    Blaze xff1a 高性能C 43 43 数学库 本文译自 xff1a Blaze A high performance C 43 43 math library Blaze是一个用于密集和稀疏算法的开源 高性能 C 43 43 数学库
  • c/c++编译:使用CMAKE进行跨平台开发

    前言 本文介绍跨平台cmake的编写 xff0c 主要是linux和windows用cmake对项目的编译 这是一个通用模板 xff0c 能够应用到更加复杂的项目中 xff0c 项目例子用https blog csdn net qq 364
  • 对于应用层HTTP协议的学习

    lt start gt 在TCP IP协议栈中 xff0c HTTP协议处于应用层 xff0c 它在最顶层进行数据报转发给应用进程 xff0c 它是最靠近用户的那一层 它的默认端口号为80 HTTP协议是基于请求响应的协议 xff0c 那么
  • 编程开发环境搭建

    全部目录 下载 amp 安装官方下载Vs2019其它历史 版本下载 开始使用安装C 43 43 的工作负载 xff08 环境 xff09 打开vs后有这些模板创建出一个控制台应用程序更多参考文档 使用手册c 43 43 参考手册Visual
  • c++创建第一个控制台程序

    目录 创建控制台应用程序打印出Hello World 空项目创建vs自带打印的创建桌面向导 自定义创建 了解代码 抛转引玉减少为什么 什么是 include 它是预处理指令什么是iostream 它是c 43 43 标准库头件 编写前的了解
  • python3-操作SQLite、创建表、添加数据、查询数据

    SQLlte数据类型 SQLite能保存什么样的数据类型 可以保存空值 整数 浮点数 字符串和blob 什么是blob xff1f xff1f 是二进制大对象 例如图片 音乐 zip文件 什么是游标 游标是在数据库中用来移动和执行查询的对象
  • 初学者都能看懂的95%置信区间

    项目github地址 xff1a bitcarmanlee easy algorithm interview and practice 经常有同学私信或留言询问相关问题 xff0c V号bitcarmanlee github上star的同学

随机推荐

  • c# WindowForm练习项目主窗体设计

    窗体分割器 SpliContainer分割器 在项目主窗体分割成左右俩部分 设置边框线属性 MonthCalendar月历控件 添加程序所需要的按钮 退出 修改密码 添加会员 按钮 固定好左边的容器 组件 ImageList 按钮太多添加图
  • C#-WinForm班级下拉框数据绑定

    前台展示 后台方法 span class hljs keyword using span System span class hljs keyword using span System Collections Generic span c
  • C#--WinForm项目主窗体设计

    主窗体基本设置 大小 颜色 去边框 出现的位置 Panel控件 背景图 颜色 布局 xff1a Label标签 文本 字体 背景颜色 布局 按钮 布局 文本 字体颜色 背景色 底部panel 绑定控件边框 颜色 用label标签导入图标 S
  • C# -- 实现WinForm程序的密码修改

    修改窗体程序密码的示例 实现分析 前台弹出修改窗体 编写后台方法 xff0c 调用通用数据访问类Update方法 数据验证 xff0c 判断原密码是否与旧密码符合 xff0c 俩次输入的新密码是否一致 更新程序全局变量 前台弹出修改窗体 编
  • C#--WinForm--表格数据控件DataGridView--绑定模式

    官方文档 DataGridView控件提供了一种强大而灵活的以表格形式显示数据的方式 用户可以使用DataGridView控件来显示少量数据的只读视图 xff0c 也可以对其进行缩放以显示特大数据集的可编辑视图 扩展DataGridView
  • ASP.NET--网站配置、发布与部署

    网站发布前的配置信息 配置文件下载 网站发布的基本步骤 写好的项目 在本机上发布 打开目录查看 xff1a 部署网站 安装IIs 打开控制面板 程序和功能 启用或关闭Windows功能 安装后 返回控制面板 管理工具 双击打开 xff1a
  • c/c++ hash表 (哈希表、字典表)

    表 1 表 存储数据 key gt value 2 表存储数据结构的困难 怎么查找 一个一个key去比较去查找 xff1f 61 61 效率不高 3 Hash算法加快查找 将字符串的key 转成整数 使用整数找到对应的value Hash算
  • c/c++ UDP通讯

    UDP通讯 1 无连接的 不需要反复的确认和握手等待 根本不关心对方是否存在 2 不可靠 可能有丢包 和先发后到 3 UDP通讯快速 占用系统资源少 4 UDP提供作为传输层协议的最基本功能 将其他的交给用户自己来管理 UDP服务端 1 创
  • c#程序流程控制与调试技术

    If选择结构 为什么要使用关系运算符 简单If 选择结构1 逻辑运算符
  • 特征融合之基于贝叶斯理论的特征融合算法

    参考文献 xff1a 1 刘渭滨 邹智元 邢薇薇 模式分类中的特征融合方法 J 北京邮电大学学报 2017 04 5 12 2 Ma A J Yuen P C Lai J H Linear Dependency Modeling for C
  • 初学者都能看懂的蒙特卡洛方法以及python实现

    项目github地址 xff1a bitcarmanlee easy algorithm interview and practice 经常有同学私信或留言询问相关问题 xff0c V号bitcarmanlee github上star的同学
  • postman安装包

    postman34位和64位安装包 xff0c 访问API 下载地址 xff1a 链接 xff1a https pan baidu com s 1p 830DPPKumXiwMPVtKYsw 提取码 xff1a 8p6k
  • STM32入门:STM32F401CDU6库函数工程文件搭建

    STM32F401CDU6库函数工程文件搭建 根据下图的结构进行复制粘贴操作 xff0c 代码部分在本文末有贴出来 xff0c STM32F4xx DSP StdPeriph Lib V1 8 0文件下载 xff08 使用part1即可 x
  • 减小vscode-cpptools的内存占用

    在VScode菜单栏文件 gt 首选项 gt 设置 搜索C Cpp intelliSenseCacheSize 修改默认的5120为512
  • Ubuntu20.04 安装tcp调试工具mNetAssist步骤

    概述 在Ubuntu20 04上安装一个比较好用的网络调试助手mNetAssist 下载链接 mNetAssist链接 提取码 vrsm 安装 进入文件 mNetAssist release amd64 deb的所在文件夹 xff0c 然后
  • 页面加载时,添加进度条,提高用户体验

    这几个月做了个项目 xff0c 在此对一些问题做一个记录 项目是前后端分离的 xff0c 前端用的 npm 43 webpack 问题 xff1a 由于系统某页面数据量过大或网络较差等原因 xff0c 导致页面还未完全加载出来 xff0c
  • matlab绘制三维图像,用ga工具箱求解有约束的优化问题(接力遗传算法)

    绘制目标函数图像 目标函数的图像绘制为 xff08 未考虑约束条件 xff09 画法1代码 有约束的三维函数图像绘制 x1 61 10 1 10 x2 61 6 1 6 x1 x2 61 meshgrid x1 x2 f 61 exp x1
  • 多元线性回归 stata

    文章目录 回归的思想 xff0c 任务 xff0c 使命 xff0c 分类线性回归一定只能用于有线性关系的变量吗数据的分类数据的标准化处理最小二乘法拟合一元线性回归方程的几个结论回归系数的解释遗漏变量会造成内生性 xff08 扰动项和变量相
  • C++编译报错:||error: ld returned 1 exit status|

    这个问题我遇到过三次了 xff0c 每次好像原因都不一样 xff0c 在网上看到很多博客都记录了这个问题 xff0c 每人原因也都不同 xff0c 所以可以有多种原因都引发这个错误 但我不懂ld returned 1 exit status
  • C++ day34 异常(三)异常规范,未捕获异常,意外异常,异常导致内存泄漏

    文章目录 异常规范 xff08 C 43 43 98添加 xff0c C 43 43 11已摒弃 xff0c 不建议使用 xff09 异常规范的作用 xff08 正是这俩作用的鸡肋和难办使它失去了粉丝 xff09 C 43 43 11支持的