C++第四十七弹---深入理解异常机制:try, catch, throw全面解析

news2024/11/15 3:24:47

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

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

2.C++异常概念

3. 异常的使用

3.1 异常的抛出和捕获

3.2 异常的重新抛出

3.3 异常安全

3.4 异常规范

4.自定义异常体系

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


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

传统的错误处理机制:

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

实际中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的处理代码
  • 2. 被选中的处理代码调用链中与该对象类型匹配且离抛出异常位置最近的那一个
  • 3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
  • 4. catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
  • 5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用,我们后面会详细讲解这个。

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

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

代码演示 

#include<iostream>
using namespace std;
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";
		//throw 1;
	else
		return ((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 (const int i)
	{
		cout << i << endl;
	}
	// 可以接受任意类型
	catch (...) 
	{
		cout << "unkown exception" << endl;
	}
	return 0;
}

运行结果 

 

3.2 异常的重新抛出

有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用
链函数来处理
,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。

代码演示

#include<iostream>
#include<string>
using namespace std;
// 重新抛出
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
	{
		throw "Division by zero condition!";
		//string str("Division by zero condition!");
		//throw str;

		//throw 1;
	}
	else
	{
		return ((double)a / (double)b);
	}
}
void Func()
{
	try
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
	}
	// 有两个类型相同异常,就近调用
	catch (const char* err)
	{
		cout << err << endl;
	}
	// 异常捕获之后后面能够一直执行
	cout << "xxxxxxxxx" << endl;
}

int main()
{
	try 
	{
		Func();
	}
	// 需要类型匹配
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const string& s)
	{
		cout << s << endl;
	}
	// 可以捕获任意类型异常
	catch (...) 
	{
		cout << "unkown exception" << endl;
	}
	return 0;
}

运行结果 

3.3 异常安全

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

3.4 异常规范

  • 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 delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;

4.自定义异常体系


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

代码演示 

#include<iostream>
#include<string>
#include<thread>
using namespace std;
// 服务器开发的异常体系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}

	virtual string what() const
	{
		return _errmsg;
	}

protected:
	string _errmsg;
	int _id;
};

// sql异常
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;
	}
};
// http异常
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((unsigned int)time(0));
	if (rand() % 7 == 0)
	{
		throw SqlException("权限不足", 100, "select * from name = '张三'");
	}
	else
	{
		cout << "执行Sql成功" << endl;
	}
}
void CacheMgr()
{
	srand((unsigned int)time(0));
	if (rand() % 5 == 0)
	{
		throw CacheException("权限不足", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException("数据不存在", 101);
	}
	else
	{
		cout << "Cache获取成功" << endl;
	}

	SQLMgr();
}
void HttpServer()
{
	// ...
	srand((unsigned int)time(0));

	if (rand() % 3 == 0)
	{
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException("权限不足", 101, "post");
	}
	else
	{
		cout << "http调用成功" << endl;
	}

	CacheMgr();
}
int main()
{
	while (1)
	{
		// 休眠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++ 提供了一系列标准的异常,定义在  #include<exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:

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

标准库代码演示

#include<iostream>
#include<vector>
using namespace std;
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;
}

运行结果 

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

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

相关文章

宠物狗检测-目标检测数据集(包括VOC格式、YOLO格式)

宠物狗检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1roegkaGAURWUVRR-D7OzzA?pwddxv6 提取码&#xff1a;dxv6 数据集信息介绍&#xff1a; 共有20580 张图像和一一对应的标注文件 标…

如何使用微软的Copilot AI工具将Word文档转换为PowerPoint

Copilot 让你可以将 Word 文档转换为 PowerPoint 演示文稿&#xff0c;使你能够以最小的努力制作出有针对性的演示文稿。这个功能是微软AI工具包的一部分&#xff0c;对于那些曾经盯着空白幻灯片不知道从何开始的人来说&#xff0c;这是一个颠覆性的改变。要充分利用这个工具&a…

libtool 中的 .la 文件说明

libtool 中的 .la 文件说明 1 概述 在 Linux 系统中&#xff0c;libtool 是一个用于自动化编译和链接复杂软件项目的工具&#xff0c;特别是那些使用了共享库&#xff08;.so 文件在 Linux 上&#xff0c;.dylib 在 macOS 上&#xff09;的项目。它帮助处理各种编译器和链接器…

快速上手 | 数据可观测性平台 Datavines 自定义SQL规则使用指南

摘要 本文主要介绍在 Datavines平台已有规则不能满足需求的情况下&#xff0c;如何通过自定义SQL规则来实现基于业务特性的数据质量检查。 规则介绍 自定义聚合SQL规则是 Datavines 平台中内置的一个灵活的规则&#xff0c;该规则允许用户通过编写SQL的方式来实现想要的数据质…

透传:利用 vercel 部署 OpenAI Proxy

透传&#xff1a;通俗理解国内ping不通国外大模型&#xff0c;需要做一层代理通过本地调用国外大模型官方的key。 一、利用 vercel 部署 OpenAI Proxy 第一步&#xff1a;Fork OpenEE 这个仓库 https://github.com/openaiee/openaiee 第二步&#xff1a;创建vercel项目 第三步…

『功能项目』战士的平A特效【35】

我们打开上一篇34武器的切换实例的项目&#xff0c; 本章要做的事情是在战士的每次按A键时在指定位置生成一个平A特效 首先将之前下载的技能拖拽至场景中 完全解压缩后重命名为AEffect 拖拽至预制体文件夹 进入主角动画的战士动画层级 双击第一次攻击 选择Animation 创建事件 …

【C++】栈和队列、优先级队列、适配器原理

目录 一.栈和队列相关接口 二.适配器介绍 三.栈和队列模拟实现 四.deque介绍 五.优先级队列 六.优先级队列的模拟实现 1.基本结构 2.插入删除操作 一.栈和队列相关接口 1.栈&#xff08;Stack&#xff09;的接口 由于栈接口只能支持栈顶插入&#xff08;入栈&#xff0…

【C语言版】数据结构教程(二)线性表

【内容简介】本文整理数据结构&#xff08;C语言版&#xff09;相关内容的复习笔记&#xff0c;供各位朋友借鉴学习。本章内容更偏于记忆和理解&#xff0c;请读者们耐心阅读。同时&#xff0c;这里提醒各位读者&#xff0c;尽管书本上说本书用的是 C 语言版&#xff0c;但是中…

基于javaweb的茶园茶农文化交流平台的设计与实现(源码+L文+ppt)

springboot基于javaweb的茶园茶农文化交流平台的设计与实现&#xff08;源码L文ppt&#xff09;4-20 系统功能结构 系统结构图可以把杂乱无章的模块按照设计者的思维方式进行调整排序&#xff0c;可以让设计者在之后的添加&#xff0c;修改程序内容的过程中有一个很明显的思维…

使用 WebStorm 导入已有的 Vue 项目并运行的步骤与注意事项

目录 1. 引言2. WebStorm 环境准备2.1 安装 WebStorm2.2 配置 Node.js 和 npm2.3 使用 nvm 管理 Node.js 和 npm 版本2.4 npm 版本与 Vue 版本对应关系 3. 导入已有的 Vue 项目3.1 打开 Vue 项目3.2 安装项目依赖3.3 使用 nvm 控制 Node.js 和 npm 版本 4. 运行 Vue 项目4.1 启…

STM32双轮平衡小车(基于STM32F103C8T6HAL库)

STM32双轮平衡小车参考教程 这个项目是跟做以上UP的STM32双轮平衡小车&#xff0c;主要是为了学习电机驱动和PID控制。这篇我就不提供源码了&#xff0c;我也是跟学的&#xff0c;原作者也提供了源码&#xff0c;我记录一下自己的理解。 1 PID原理 1.1 PID简介 1.2 PID演示 …

打破AI壁垒-降低AI入门门槛

AI和AGI AI&#xff08;人工智能-Artificial Intelligence&#xff09;&#xff1a; 先说说AI&#xff0c;这个大家可能都不陌生。AI&#xff0c;就是人工智能&#xff0c;它涵盖了各种技术和领域&#xff0c;目的是让计算机模仿、延伸甚至超越人类智能。想象一下&#xff0c;…

图像分割分析效果2

这次加了结构化损失 # 训练集dice: 0.9219 - iou: 0.8611 - loss: 0.0318 - mae: 0.0220 - total: 0.8915 # dropout后&#xff1a;dice: 0.9143 - iou: 0.8488 - loss: 0.0335 - mae: 0.0236 - total: 0.8816 # 加了结构化损失后:avg_score: 0.8917 - dice: 0.9228 - iou: 0.…

如何做服务迁移、重构?

思维导图 0. 前言 本文意在提供服务迁移的完整思路&#xff0c;将思考题变成填空题&#xff0c;只需要按照本文提供的思路填空&#xff0c;服务迁移至少可以做到 80 分。 本文的服务迁移指&#xff1a;将老服务的代码迁移至新服务。 1. 服务资源梳理 服务资源&#xff0c;我…

AI学习记录 - 旋转位置编码

创作不易&#xff0c;有用点赞&#xff0c;写作有利于锻炼一门新的技能&#xff0c;有很大一部分是我自己总结的新视角 1、前置条件&#xff1a;要理解旋转位置编码前&#xff0c;要熟悉自注意力机制&#xff0c;否则很难看得懂&#xff0c;在我的系列文章中有对自注意力机制的…

Win32函数调用约定(Calling Convention)

平常我们在C#中使用DllImportAttribute引入函数时&#xff0c;不指明函数调用约定(CallingConvention)这个参数&#xff0c;也可以正常调用。如FindWindow函数 [DllImport("user32.dll", EntryPoint"FindWindow", SetLastError true)] public static ext…

来啦| LVMH路威酩轩25届校招智鼎高潜人才思维能力测验高分攻略

路威酩轩香水化妆品(上海)有限公司是LVMH集团于2000年成立&#xff0c;负责集团旗下的部分香水化妆品品牌在中国的销售包括迪奥、娇兰、纪梵希、贝玲妃、玫珂菲、凯卓、帕尔马之水以及馥蕾诗等。作为目前全球最大的奢侈品集团LVMH 集团秉承悠久的历史&#xff0c;不断打破常规&…

群晖最新版(DSM 7.2) 下使用 Web Station 部署 flask 项目

0. 需求由来 为了在 DSM 7.2 版本下的群晖 NAS 里运行我基于 flask 3.0.2 编写的网页应用程序&#xff0c;我上网查了非常多资料&#xff0c;也踩了很多坑。最主要的就是 7.2 版本的界面与旧版略有不同&#xff0c;而网络上的资料大多基于旧版界面&#xff0c;且大部分仅仅说明…

记忆化搜索【下】

375. 猜数字大小II 题目分析 题目链接&#xff1a;375. 猜数字大小 II - 力扣&#xff08;LeetCode&#xff09; 题目比较长&#xff0c;大致意思就是给一个数&#xff0c;比如说10&#xff0c;定的数字是7&#xff0c;让我们在[1, 10]这个区间猜。 如果猜大或猜小都会说明…

2024AI绘画工具排行榜:探索最受欢迎的AI绘图软件特点与选择指南

AI绘画工具各有优势&#xff0c;从开放性到对特定语言和文化的支持&#xff0c;以及对图像细节和艺术性的不同关注点&#xff0c;根据具体需求选择合适的工具 MidJourney 图片品质卓越&#xff0c;充满独特创意&#xff0c;初期能够免费获取数十账高质量图片&#xff0c;整个生…