【C++】异常处理

news2024/11/22 22:39:37

目录

一、C语言中传统的异常处理方式:

二、C++中的异常处理方式:

三、异常的使用

1、关于抛出与捕获:

2、关于异常的抛出和匹配:

3、异常的重新抛出:

4、异常安全:

5、异常规范:

四、异常的优缺点:

1、优点:

2、缺点:


一、C语言中传统的异常处理方式:

当C语言程序出现错误的时候会出现两种情况:

1、在程序结束时返回错误码(一般如果正常结束返回0)

2、在程序进行时直接发现错误直接进行终止(比如assert)

以上就是断言错误或者是错误码返回,

在断言错误中它会自己报出错具体位置和出错原因,就可以定位出错位置

注意:这是在debug的环境下才会有assert,如果在release版本会自动删除assert

二、C++中的异常处理方式:

在C++中,祖师爷觉得C语言的那些方式依然给的信息不够,所以就创造出来了抛异常,捕获异常这些概念,在C++中引入了三个关键字来进行关于程序异常的信息捕获。

1、try:

这个就是在try的代码段中进行监视的,如果有,就会进行抛出,捕获异常。

try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。

try {
    //在这代码区域中进行监视,如果有异常就进行抛出
}
catch (const exception& e) {
    // 处理异常
}

2、throw:

这个就是抛出异常,

3、catch:

这个就是将抛出的异常捕获到

在try后面还可以进行多次catch的接收:

try
{
    //保护代码
}
catch (const exception& e)
{}
catch (const exception& e)
{}
catch (const exception& e)
{}

三、异常的使用

1、关于抛出与捕获:

double Division(int a, int b)
{
	if (b == 0)
		throw("被除数不能为零");
	else
		return (float)a / (float)b;

}

void func()
{
	double x, y;
	cin >> x>> y;
	cout << Division(x,y) << endl;
}

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

如果传输有问题的话就会catch,否则就正常返回

2、关于异常的抛出和匹配:

1、如果在不同函数中有多个匹配的,就会找离catch最近的那一层的catch,并且返回catch后,会继续执行catch后面的语句。

double Division(int a, int b)
{
	if (b == 0)
		throw("被除数不能为零");
	else
		return (float)a / (float)b;

}

void func()
{
	double x, y;
	cin >> x>> y;
	try
	{
		cout << Division(x,y) << endl;
	}
	catch(const int* err)
	{
		cout << "catch(const int* err)" << endl;
		cout << err << endl;
	}
	catch(const int err)
	{
		cout << "catch(const int err)" << endl;
		cout << err << endl;
	}
	catch (const char* err)
	{
		cout << "catch (const char* err)" << endl;
		cout << err << endl;
	}
	cout << "void func()" << endl;
}

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

2、如果有多个catch就会找类型相同的catch语句进行执行,如果没有找到就会报错

3、那么就有一种方法:

catch(...)这样来进行捕获,这个是捕获任意类型的,作为程序异常捕获的最后一道防线,避免程序因为没有找到异常匹配而挂了

4、在抛出的过程中,如果跨越了多个函数后找到捕获了,就会直接跳转到对应地方,并不会一层一层地返回。

5、在实际情况中,异常并不是传这种内置类型的,而是通过派生类继承基类来传派生类这种的自定义类型的,所以实际中都会定义一套继承的规范体系。 这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了

// 服务器开发中通常使用的异常继承体系
//作为父类
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 = "SQL异常:";
		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 = "缓存异常:";
		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 = "HttpServer异常:";
		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();
}
int main()
{
	while (1)
	{
		Sleep(500);
		try {
			HttpServer();
			CacheMgr();
			SQLMgr();
		}
			catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "未知异常" << endl;
		}
	}
	return 0;
}

3、异常的重新抛出:

在抛出异常时,毕竟是直接跳转的,在中间省略的函数中可能会存在内存泄漏问题,如下,在func函数中,其开辟的数组就不会被释放。

//异常信息类
class Exception
{
public:
	Exception(int errcode, const string& content)
		:_errno(errcode), _content(content)
	{}

	string what() const
	{
		return to_string(_errno) + " : " + _content;
	}
public:
	int _errno = 0;//作为错误编号,在实际中方便查找
	string _content;//记录错误信息
};

double Division(int a, int b)
{
	if (b == 0)
	{
		Exception e(1, "被除数不能为0");
		throw e;
	}
	else
		return (float)a / (float)b;

}

void func()
{
	double x, y;
	cin >> x>> y;
	int* arr = new int[10];

	cout << Division(x,y) << endl;

	delete[] arr;
	cout << "delete[] arr: " << arr << endl;
	
	cout << "void func()" << endl;
}

int main()
{
	try
	{
		func();
	}
	catch(const Exception& err)
	{
		cout << "int main()" << endl;
		err.what();
	}
	catch (...)
	{
		cout << "int main()" << endl;
		cout << "未知错误" << endl;
	}
	return 0;
}

解决方法:

要想正确的释放内存,需要在func函数中主动捕获异常,将空间释放后,重新抛出异常,

所以func函数就需要改进为:

void func()
{
	int* arr = new int[10];
	try {
		int x, y;
		cin >> x >> y;
		cout << Division(x, y) << endl;
	}
	catch (...) {
		delete arr;
		throw;
	}
}

这里捕获异常后并不处理异常,异常还是交给main处理,这里捕获了再重新抛出去,

注意:这里捕获了什么类型的异常就抛出什么类型的异常。

4、异常安全:

1、构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
2、析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏

3、C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,但是才C++11中引入了智能指针来解决这一问题,使用智能指针可以有效地避免因异常和忘记释放内存导致的资源泄漏

5、异常规范:

1、异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的 后面接throw(类型),列出这个函数可能抛掷的所有异常类型。

2、函数的后面接throw(),表示函数不抛异常。

3、若无异常接口声明,则此函数可以抛掷任何类型的异常。

void func1() throw(int, char, string); // 可能抛出这三种类型的异常

void func2() throw(); // 该函数不会抛出异常

void func3(); // 该函数可以抛出任何类型的异常

但是在C++11中引入了noexcept关键字,这样只需标记这个函数不会抛出异常,

但是如果对标记后的函数进行抛异常了,进程就会直接被终止。

四、异常的优缺点:

1、优点:

1、如果使用好了可以展示更多的错误信息,能够更好的定位bug

2、 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么就得层层返回错误,最外层才能拿到错误,而如果在最外层捕获,异常就是直接返回到最外层,不用层层返回。

3、 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常

4、部分函数使用异常更好表示错误,比如 T& operator[](size_t pos) 如果越界了就抛异常,而不是返回 T() 或 断言。

2、缺点:

1、在进行调试的时候代码流的跨度大,导致跟踪程序比较困难

2、因为捕获异常捕获的是一份临时拷贝,所以存在性能开销,但是现如今的设备几乎可以忽略不计

3、C++的标准库异常体系定义的不好,导致各自定义各自的比较混乱

4、异常尽量规范使用:

        a、抛出异常类型都继承自一个基类

        b、函数是否抛异常、使用关键字noexcept的方式规范化

但是异常总之是利大于弊的,在工程中也尽量按规范使用异常 

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

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

相关文章

idea 同一个项目不同模块如何设置不同的jdk版本

在IntelliJ IDEA中&#xff0c;可以为同一个项目中的不同模块设置不同的JDK版本。这样做可以让你在同一个项目中同时使用多个Java版本&#xff0c;这对于需要兼容多个Java版本的开发非常有用。以下是设置步骤&#xff1a; 打开项目设置&#xff1a; 在IDEA中&#xff0c;打开你…

Git 下载及安装超详教程(2024)

操作环境&#xff1a;Win 10、全程联网 一、什么是Git&#xff1f; Git 是一个开源的分布式版本控制系统&#xff0c;由 Linus Torvalds 创立&#xff0c;用于有效、高速地处理从小到大的项目版本管理。Git 是目前世界上最流行的版本控制系统&#xff0c;被广泛用于软件开发中…

论文翻译 | Generated Knowledge Prompting for Commonsense Reasoning

摘要 整合外部知识是否有利于常识推理&#xff0c;同时保持预训练序列模型的灵活性&#xff0c;这仍然是一个悬而未决的问题。为了研究这个问题&#xff0c;我们开发了生成知识提示&#xff0c;它包括从语言模型生成知识&#xff0c;然后在回答问题时提供知识作为附加输入。我们…

TCP ---滑动窗口以及拥塞窗口

序言 在上一篇文章中我们介绍了 TCP 中的协议段格式&#xff0c;以及保证其可靠传输的重传机制&#xff0c;着重介绍了三次握手建立连接&#xff0c;四次挥手断开连接的过程(&#x1f449;点击查看)。  这只是 TCP 保证通信可信策略的一部分&#xff0c;现在让我们继续深入吧&…

盲拍合约:让竞拍更公平与神秘的创新解决方案

目录 前言 一、盲拍合约是什么&#xff1f; 二、盲拍合约工作原理 1、合约创建与初始化 2、用户出价&#xff08;Bid&#xff09; 3、出价结束 4、披露出价&#xff08;Reveal&#xff09; 5、处理最高出价 6、结束拍卖 7、退款与提款 三、解析盲拍合约代码…

02:(寄存器开发)流水灯/按键控制LED

寄存器开发 1、LED流水灯2、按键控制LED 1、LED流水灯 通过第一章的学习&#xff0c;我们已然知晓了LED的点亮和熄灭的方式&#xff0c;下面学习流水灯的制作流程。 流水灯呈现的样子&#xff1a;就是第一个LED灯点亮&#xff0c;延迟一段时间&#xff0c;第一个LED灯熄灭第二…

2020大厂web前端面试常见问题总结

本篇收录了一些面试中经常会遇到的经典面试题以及自己面试过程中遇到的一些问题。通过对本篇知识的整理以及经验的总结&#xff0c;希望能帮到更多的前端面试者。 1.web前端项目的结构是怎样的&#xff1f;文件有哪些命名规范&#xff1f; 项目结构规范 页面文件&#xff1a;以…

树莓派5:换源(针对Debian12)+安装包管理器Archiconda(图文教程+详细+对初学者超级友好)

目录 一、安装官方发行版系统&#xff08;Debian&#xff09;二、换源&#xff08;记得参考上述教程ssh连接到树莓派Terminal&#xff0c;or外接一块Hdmi显示屏&#xff09;2.1 查看自己安装的树莓派镜像架构2.2 查询自己的系统版本2.3 打开清华大学开源软件镜像站网站2.3.1 传…

浅析Golang的Context

文章目录 1. 简介2. 常见用法2.1 控制goroutine的生命周期&#xff08;cancel&#xff09;2.2 传递超时&#xff08;Timeout&#xff09;信息2.3 传递截止时间&#xff08;Deadline&#xff09;2.4 传递请求范围内的全局数据 &#xff08;value&#xff09; 3 特点3.1 上下文的…

FWA(固定无线接入),CPE(客户终端设备)简介

文章目录 FWA&#xff08;Fixed Wireless Access&#xff09;&#xff0c;固定无线接入CPE&#xff08;Customer Premise Equipment&#xff09;&#xff0c;用户驻地设备 FWA&#xff08;Fixed Wireless Access&#xff09;&#xff0c;固定无线接入 固定无线接入&#xff08…

Geogebra009—构建正六边形

继续巩固一下基础&#xff0c;本篇我们来做一个正六边形 目录 一、成品展示二、涉及内容三、做图步骤1. 绘制一个以A点为圆心过B点的圆circle1&#xff1b;2. 以B点为圆心过A点绘制另外一个圆circle2&#xff1b;3. 绘制两个圆的交点&#xff0c;得到顶点C和D&#xff1b;4. 以…

Leetcode: 0001-0010题速览

Leetcode: 0001-0010题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…

奔驰GLS450升级原厂电吸门效果怎么样

奔驰GLS450升级原厂电吸门后&#xff0c;能带来以下效果&#xff1a; • 关门更优雅&#xff1a;只需轻轻推车门到基本关闭的位置&#xff0c;当车门距离车门锁大约6毫米时&#xff0c;传感器便会启动电动马达将车门安静地拉入&#xff0c;然后固定住&#xff0c;告别传统关门…

HTML+CSS基础用法介绍五

目录&#xff1a; 结构伪类选择器盒子模型-边框线盒子模型-内边距盒子模型-解决盒子被撑大盒子模型-外边距与版心居中小知识&#xff1a;清除浏览器中所有标签的默认样式内容溢出控制显示方式盒子模型-圆角 &#x1f40e;正片开始 结构伪类选择器 什么是结构伪类选择器&…

18.安卓逆向-frida基础-调试实战2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

Windows UAC权限详解以及因为权限不对等引发软件工具无法正常使用的实例分析

目录 ​1、什么是UAC&#xff1f; 2、微软为什么要设计UAC&#xff1f; 3、标准用户权限与管理员权限 4、程序到底以哪种权限运行&#xff1f;与哪些因素有关&#xff1f; 4.1、给程序设置以管理员权限运行的属性 4.2、当前登录用户的类型 4.3、如何通过代码判断某个进程…

智能 AI 写作软件:开启创作新纪元

不论你在哪行哪业应该都躲不开写作这件事被。写作已经成为了我们生活和工作中不可或缺的一部分。随着人工智能技术的飞速发展&#xff0c;AI 智能写作工具应运而生。接下来&#xff0c;让我们一起揭开智能ai写作工具的神秘面纱。 1.笔灵AI写作 直通车&#xff1a;https://ibi…

②EtherCAT转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherCAT 转 Modbus TCP (接上一章&#xff09; GW系列型号 配置说明 上载 网线连接电脑到模块上的 WEB 网页设置网口&#…

论文笔记:Online Class-Incremental Continual Learning with Adversarial Shapley Value

这篇工作的focus 是 memory-based approach 1. 挑战/问题&#xff1a; 灾难性遗忘&#xff1a;深度神经网络在学习新任务时往往会忘记先前任务的知识。内存和计算效率&#xff1a;在个人设备上执行深度学习任务时&#xff0c;需要最小化内存占用和计算成本。数据流增量学习&am…