C++学习记录——이십구 异常

news2024/12/28 3:06:08

文章目录

  • 1、异常概念
  • 2、实际用法
  • 3、C++标准库的异常体系
  • 4、重新抛出异常
  • 5、优缺点


1、异常概念

C语言处理错误有assert,返回错误码来处理错误的方式,不过release模式下assert无效,错误码需要程序员自己去查看是什么错误。

C++认为应当能给到程序员一个更明确的错误,所以就出现了异常处理。当出现错误时,C++会抛一个对象,里面包含错误信息。

异常有3个关键字,throw抛出异常,try和catch捕获异常。

#include <iostream>
using namespace std;

double Division(int a, int b)
{
	if (b == 0)
		throw "Division by zero condition!";
	else
		return ((double)a / (double)b);
}

void Func()
{
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}

int main()
{
	Func();
	return 0;
}

如果没有异常,那就正常运行,如果有异常,必须被捕获,否则就只会终止程序并弹出错误窗口。捕获的位置不一定只包含有错误的代码块。捕获一般在外部捕获。

int main()//也可以写在Func函数里。不过通常写在最外面
{
	try
	{
		Func();
	}
	catch (const char* str)
	{
		cout << str << endl;
	}
	return 0;
}

当出现异常后,throw会直接跳到catch处。catch只会捕获try里的代码。如果Func和main都有捕获,那么出异常会去哪里?会跳到Func函数里,它会跳到离throw最近的那个位置,然后继续执行跳到的位置的后面的代码,之后所有的捕获就不用再捕获了。

异常出现后先在异常所在的栈帧里查找catch,没有就结束这个栈帧,然后到上一层栈帧里去查找catch,还是没有结束这个栈帧,继续向上找。

捕获时参数也得匹配上类型,比如Func和main都有捕获的话,throw就会到const char*类型的那个catch处,会找匹配异常的catch,如果全都没有,那就终止程序,弹出错误窗口,因为没有捕获的,相当于只有throw,没有catch,那就直接报错。

2、实际用法

写的时候不能像上面那样粗糙,要写一个类来捕获。

class Exception
{
private:
	int _errid;//错误码
	string _errmsg;//错误描述
};

为什么要这么写?实际的程序中,出现异常并不是一定要立即报出来的,比如发消息,如果网络不好,发送失败,那就会重试好几次,直到到达阈值,如果还是失败,那就返回异常。

写一个伪代码

void TrySendMsg()
{
	try
	{
		SendMsg();
	}
	catch(const Exception& e)
	{
		if (e.getErrid() == 3)//假设的一个错误码数字
		{
			//重试
		}
		else
		{
			//记录日志,界面展示错误信息
		}
	}
}

回到我们的代码就这样写

class Exception
{
public:
	Exception(int errid, const string& msg)
		:_errid(errid)
		, _errmsg(msg)
	{}

	const string& GetMsg() const
	{
		return _errmsg;
	}

	int GetErrid() const
	{
		return _errid;
	}
private:
	int _errid;//错误码
	string _errmsg;//错误描述
};

double Division(int a, int b)
{
	if (b == 0)
	{
		Exception err(1, "除0错误");
		throw err;
	}
	else
		return ((double)a / (double)b);
}

void Func()
{
	int len, time;
	cin >> len >> time;
	try
	{
		cout << Division(len, time) << endl;
	}
	catch (char str)//无法匹配异常
	{
		cout << str << endl;
	}
	cout << "Func()" << endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (const Exception& e)
	{
		//看个人需求来处理
		cout << e.GetMsg() << endl;
	}
	return 0;
}

抛出的是局部对象err,编译器会拷贝一份,将拷贝的传给下面的e,catch完后拷贝的那份就结束生命周期了。

如果想捕获自己不知道什么类型的异常,那就这样写

	try
	{
		Func();
	}
	catch (const Exception& e)
	{
		//看个人需求来处理
		cout << e.GetMsg() << endl;
	}
	catch(...)
	{
		cout << "未知异常" << endl;
	}

三个点就可以接收所有。try和catch可以套上循环。未知异常是一种底线,防止错误没被捕获到。

如果异常类型很多,不同的抛异常需求,还有很多未知异常,这如何处理?可以抛出派生类对象,用基类捕获,派生类转换到基类是天然的转换,是用切割做到的。Exception是基类,带着基础信息,其它程序员添加自己的需求来作为派生类,这里也可以用多态。

写一个模拟用继承和多态来接收类的代码

class Exception
{
public:
	Exception(int errid, const string& msg)
		:_errid(errid)
		, _errmsg(msg)
	{}

	const string& what() const
	{
		return _errmsg;
	}

	int GetErrid() const
	{
		return _errid;
	}
protected:
	int _errid;//错误码
	string _errmsg;//错误描述
};

//每个子类都写一个what,形成多态
class SException : public Exception
{
public:
	SException(const string& msg, int errid, const string& s)
		:Exception(errid, msg)
		, _s(s)
	{}

	virtual string what() const
	{
		string str = "SException:";
		str += _errmsg;
		str += "->";
		str += _s;
		return str;
	}
protected:
	string _s;
};

class CException : public Exception
{
public:
	CException(const string& errmsg, int id)
		:Exception(id, errmsg)
	{}

	virtual string what() const
	{
		string str = "CException:";
		str += _errmsg;
		return str;
	}
};

class HSException : public Exception
{
public:
	HSException(const string& errmsg, int id, const string& type)
		:Exception(id, errmsg)
		, _type(type)
	{}

	virtual string what() const
	{
		string str = "SException:";
		str += _errmsg;
		str += _type;
		str += _errmsg;
		return str;
	}
private:
	const string _type;
};

void SMgr()
{
	srand(time(0));
	if (rand() % 7 == 0)
	{
		throw SException("权限不足", 100, "select * from name = '张三'");
	}
	cout << "调用成功" << endl;
}

void CMgr()
{
	srand(time(0));
	if (rand() % 5 == 0)
	{
		throw CException("权限不足", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CException("数据不存在", 101);
	}
	SMgr();
}

void HServer()
{
	//模拟服务出错
	srand(time(0));
	if (rand() % 3 == 0)
	{
		throw HSException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HSException("权限不足", 101, "post");
	}
	CMgr();
}


int main()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));
		try
		{
			HServer();
		}
		catch (const Exception& e)
		{
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
	return 0;
}

3、C++标准库的异常体系

库中的是exception。

在这里插入图片描述

在这里插入图片描述
每个异常都会找到与它最匹配的那个处理方式,直到每个异常都处理完,如果找遍了所有的也还没有处理,那就弹出错误窗口。

C++98的标准里,如果在声明一个函数后,写上throw(),就像之前写const的位置,那么这个throw()就表明这个函数不抛异常。如果throw()括号里写上异常类型,那就表明这个函数只抛这些类型的异常。实际上这个规范通常不用。

C++11中有新规则,它仍然兼容98。一个函数明确不抛异常的话,就在原先写throw()的位置写上noexcept,比如void Func() noexcept,但是还是要抛的话,那就会出错,执行不了;可能抛异常,那就什么都不写。

最好不要在构造函数抛异常,可能导致没有初始化完成。

像new/malloc/fopen/lock时不要抛异常,它们需要delete/free/fclose/unlock,因为会导致内存泄漏,文件未关闭、死锁等问题。这方面可以用智能指针来更好地解决。

4、重新抛出异常

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];
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;

	cout << "delete []" << array << endl;
	delete[] array;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	return 0;
}

Func里可以看到如果发生除0错误抛出异常,下面的array就没有得到释放。所以捕获异常后应当不处理异常,异常交给外面处理,这里捕获了再重新抛出去。

void Func()
{
	int* array = new int[10];
	int len, time;
	cin >> len >> time;
	try
	{
		cout << Division(len, time) << endl;
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	cout << "delete []" << array << endl;
	delete[] array;
}

不过我们仍然可以交给main来处理,Func那里抓到异常后,先执行后面的,然后再次throw,就是重新抛出异常。

void Func()
{
	int* array = new int[10];
	int len, time;
	cin >> len >> time;
	try
	{
		cout << Division(len, time) << endl;
	}
	catch (const char* errmsg)
	{
		cout << "delete []" << array << endl;
		delete[] array;
		throw errmsg;
	}
}

还应当在main里加上捕获未知异常。如果Func里有调用多个不同的函数,那么可以写上捕获所有的catch,也就是catch(…)。不过捕获异常最好全都在一起,比如main里,方便记录。

5、优缺点

优点:
1、相比错误码,能更清晰地展示各种错误信息,附带各种需要的数据
2、错误码是层层返回的
3、第三方库的异常需要捕捉
4、部分函数使用异常更好处理,比如构造函数没有返回值,越界等问题。、

缺点:
1、异常容易乱跳,导致程序的执行流乱跳,调试时会直接跳到catch处
2、异常容易导致内存泄漏、死锁等安全问题,C++也没有垃圾回收机制
3、C++标准库的异常体系不怎么好,一般都使用自定义的异常体系
4、异常需要规范起来,否则捕获就很不容易。抛出的异常类型都继承一个基类,以及无论抛什么异常都用noexcept等来规范起来

本篇gitee

结束。

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

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

相关文章

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略

LLMs之Code&#xff1a;Code Llama的简介、安装、使用方法之详细攻略 导读&#xff1a;2023年08月25日(北京时间)&#xff0c;Meta发布了Code Llama&#xff0c;一个可以使用文本提示生成代码的大型语言模型(LLM)。Code Llama是最先进的公开可用的LLM代码任务&#xff0c;并有潜…

会玩这10个Linux命令,一定是个有趣的IT男!

Linux当中有很多比较有趣的命令&#xff0c;可以动手看看&#xff0c;很简单的。 1、rev命令 一行接一行地颠倒所输入的字符串。 运行&#xff1a; $rev 如输入&#xff1a;shiyanlou shiyanlou 2、asciiview命令 1)先安装aview $sudo apt-get install aview 2)再安装…

第八周第二天学习总结 | MySQL入门及练习学习第四天

实操练习&#xff1a; 1.建立一个员工表和与之对应的部门表 2.建立外键约束 3.使用多表查询&#xff0c;直接查询部门表和员工表 发现&#xff1a;有很多多余的因笛卡尔乘积而带来的多余输出内容 我想要的到简单明了的数据结果&#xff0c;要消除多于因笛卡尔乘积带来的输出…

3d激光slam建图与定位(2)_aloam代码阅读

1.常用的几种loam算法 aloam 纯激光 lego_loam 纯激光 去除了地面 lio_sam imu激光紧耦合 lvi_sam 激光视觉 2.代码思路 2.1.特征点提取scanRegistration.cpp&#xff0c;这个文件的目的是为了根据曲率提取4种特征点和对当前点云进行预处理 输入是雷达点云话题 输出是 4种特征点…

【Apollo学习笔记】——规划模块TASK之PATH_ASSESSMENT_DECIDER

文章目录 前言PATH_ASSESSMENT_DECIDER功能简介PATH_ASSESSMENT_DECIDER相关信息PATH_ASSESSMENT_DECIDER总体流程1. 去除无效路径2. 分析并加入重要信息给speed决策SetPathInfoSetPathPointType 3. 排序选择最优的路径4. 更新必要的信息 前言 在Apollo星火计划学习笔记——Ap…

信息系统项目管理师(第四版)教材精读思维导图-第七章项目立项管理

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 本章思维导图PDF格式 本章思维导图XMind源文件 ​ 目录 7.1 项目建议与立项申请 7.2 项目可…

【洛谷】P1873 EKO / 砍树

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1873 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 整体思路&#xff1a;二分答案 设置一个变量highest来记录最高的树的高度&#xff0c;sum记录切下的木头的长度。令左边界l0&#xff0c…

java八股文面试[多线程]——公平锁

一个线程启动时刚好碰到另外的线程释放锁&#xff0c;则该线程会获取到锁&#xff0c;其他等待队列中的线程不会获取到锁。好处&#xff1a;减少线程状态切换&#xff08;不用在start()之后进入阻塞&#xff09;&#xff0c;提高吞吐量。 非公平锁 非公平锁是多个线程加锁时直接…

高通开发系列 - QTI守护进程服务介绍

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 代码位置和依赖关系功能介绍代码逻辑讲解外设节点关注的目录socket服务端初始化DPM客户端监听守护关键的数据结构体…

C# char曲线控件

一、char曲线显示随机数数据 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Syst…

【JS案例】JS实现手风琴效果

JS案例手风琴 &#x1f31f;效果展示 &#x1f31f;HTML结构 &#x1f31f;CSS样式 &#x1f31f;实现思路 &#x1f31f;具体实现 1.绑定事件 2.自定义元素属性 3.切换菜单 &#x1f31f;完整JS代码 &#x1f31f;写在最后 &#x1f31f;效果展示 &#x1f31f;HTML…

如何使用pytest进行自动化测试

Pytest作为广泛使用的Python测试框架之一&#xff0c;可以用于单元测试、功能测试、性能测试等场合。自动化测试是功能测试的一种形式&#xff0c;可以使用Pytest编写并管理自动化测试用例&#xff0c;再执行相应的自动化测试。 功能测试通常包括接口测试和Web测试两种类型&am…

服务器端使用django websocket,客户端使用uniapp 请问服务端和客户端群组互发消息的代码怎么写的参考笔记

2023/8/29 19:21:11 服务器端使用django websocket,客户端使用uniapp 请问服务端和客户端群组互发消息的代码怎么写 2023/8/29 19:22:25 在服务器端使用Django WebSocket和客户端使用Uniapp的情况下&#xff0c;以下是代码示例来实现服务器端和客户端之间的群组互发消息。 …

使用GoLand进行远程调试

对部署进行配置 在此配置远程服务器地址&#xff0c;映射&#xff0c;是否自动上传(更新)等 选择SFTP类型 选择上传 另外给自动上传选项打钩 此时在本地修改某个文件&#xff0c;远程机器相应目录的文件&#xff0c;也会被同步修改 对远程调试进行配置 远程机器需要安装delve 而…

桃子叶片病害识别(图像连续识别和视频识别,Python代码,pyTorch框架,深度卷积网络模型,很容易替换为其它模型,带有GUI识别界面)

桃子叶片病害识别&#xff08;图像连续识别和视频识别&#xff0c;Python代码&#xff0c;pyTorch框架&#xff0c;深度卷积网络模型&#xff0c;很容易替换为其它模型&#xff0c;带有GUI识别界面&#xff09;_哔哩哔哩_bilibili 1.数据集分为三类 健康的桃子叶片 &#xff0c…

LeetCode(力扣)530. 二叉搜索树的最小绝对差Python

LeetCode530. 二叉搜索树的最小绝对差 题目链接代码 题目链接 https://leetcode.cn/problems/minimum-absolute-difference-in-bst/ 代码 递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # …

React【React是什么?、创建项目 、React组件化、 JSX语法、条件渲染、列表渲染、事件处理】(一)

文章目录 React是什么&#xff1f; 为什么要学习React React开发前准备 创建React项目 React项目结构简介 React组件化 初识JSX 渲染JSX描述的页面 JSX语法 JSX的Class与Style属性 JSX生成的React元素 条件渲染&#xff08;一&#xff09; 条件渲染 &#xff0…

浅谈卫星通信技术

目录 1.卫星的概念 2.卫星的具体作用 3.利用卫星进行通信的优势 4.卫星通信带来的技术变革 1.卫星的概念 卫星是指在地球轨道上运行的天体或人造物体。一般来说&#xff0c;我们所说的卫星主要指人造卫星&#xff0c;它是由人类设计、制造并送入轨道的人造宇宙飞行器。 人造…

海思SS528V100 开发环境搭建记录

1.拿到厂家的SDK 解压rar压缩包(aarch64-mix210-linux.tga 要用tar -zxvf命令解压)之后会得到三个文件夹 如下图高亮了 2.安装交叉编译工具链 tar -zxf aarch64-mix210-linux.tgz 解压文件&#xff0c;进入 aarch64-mix210-linux 目录&#xff0c;运行 chmod x aarch64-mix2…

如何实现Python自动化测试

Python自动化测试常用于Web应用、移动应用、桌面应用等的测试&#xff0c;在这我也准备了一份软件测试面试视频教程&#xff08;含接口、自动化、性能等&#xff09;&#xff0c;需要的可以直接在下方观看&#xff0c;你也直接点击文末小卡片免费领取资料文档 软件测试面试视频…