C++ 异常

news2024/11/25 7:16:05

在这里插入图片描述

文章目录

  • 一. C语言的错误处理
  • 二. C++的异常
  • 三. 异常的抛出与捕获
    • 1. 异常的抛出与匹配机制
    • 2. 在函数调用链中异常栈展开匹配原则
    • 3. 异常安全
  • 四. 多态在异常中的应用
  • 五. C++标准库的异常体系
  • 六. 异常规范
  • 七. 异常的优缺点
  • 结束语

一. C语言的错误处理

在C语言中,我们常见的处理错误的方式就是assert,不过其方式较为暴力,会直接终止程序。
在Linux操作系统中,退出码也是常见的记录错误信息的方式,不过需要我们自己去查找错误原因

二. C++的异常

C++在C语言的基础上,觉得单单返回退出码较为单调,信息不足,所以提出了异常的概念

异常的概念:

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

异常的三个关键字:

  1. throw:当问题出现时,可以通过throw关键字抛出,抛出的异常可以是任何类型
  2. try:try块中的代码,代表其可能会抛出异常,需要进行捕获,其后通常跟着一个或者多个catch块
  3. catch:通过catch捕获可能抛出的异常,如果捕捉成功,即可执行catch块中的代码(处理方式)

如果一个块抛出一个异常,就需要try,catch语句捕获。try块中放置可能抛出异常的代码,catch块中放置处理方式等等。try块中的代码被称为保护代码


三. 异常的抛出与捕获

接下来我们使用除零错误演示以上三个关键字的使用

#include<iostream>
using namespace std;

//除法函数
double division(double a, double b)
{
	if (b == 0)//除零异常,抛出异常
		throw "Divide by zero error";

	return a / b;
}
//除法函数调用者
void Func()
{
	while (1)
	{
		double a, b;
		cin >> a >> b;
		cout << division(a, b) << endl;;

		cout << "void Func()" << endl;
	}
}
//主函数
int main()
{
	//捕获
	try
	{
		Func();
	}
	catch (const char*str)
	{
		cout << str << endl;
	}


	return 0;
}

一次运行结果如下:
在这里插入图片描述

当没有出现除零异常时,while循环一直进行,“void Func()”也成功打印
但出现除零异常时,代码运行直接跳转到了main函数的catch部分,并且str成功捕捉到了throw的字符串
在这里插入图片描述

1. 异常的抛出与匹配机制

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
  2. 选中的处理代码时调用链中与该对象类型匹配且离抛出异常位置最近的那一个
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在catch后销毁(这里的处理类似于函数的传值返回)
  4. catch(...)可以捕获任意类型的异常,…是可变参数包。但问题是不知道异常错误是什么
  5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配。如果使用多态,即抛出派生类对象,然后使用基类捕获,这个在实际中非常实用。

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

  1. 首先检查throw本身是否处于try块内部,如果是,匹配适合的catch语句。如果有匹配的,则跳转到catch的地方进行处理
  2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
  3. 如果到达main函数的栈,还是没有匹配的,则报错终止程序——异常必须被捕获。
  4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行

上述沿着调用链查找匹配的catch语句的过程称为栈展开。所以实际中,为了防止未知异常终止程序,我们可以在最后加上catch(…),捕获任意类型的异常

异常没有被捕获

在这里插入图片描述

捕获匹配原则

还是除零异常

#include<iostream>
using namespace std;

//除法函数
double division(double a, double b)
{
	if (b == 0)
		throw "Divide by zero error";

	return a / b;
}
//除法函数调用者
void Func()
{
	while (1)
	{
		double a, b;
		cin >> a >> b;
		try
		{
			cout << division(a, b) << endl;
		}
		catch (const char*str)
		{
			cout << str << endl;
		}

		cout << "void Func()" << endl;
	}
}
//主函数
int main()
{
	//捕获
	try
	{
		Func();
	}
	catch (const char*str)
	{
		cout << str << endl;
	}


	return 0;
}

如果Func()中也有匹配的catch语句,那么就会直接在Func中被捕获,不会再跳转到main函数中。
成功捕获后,会执行catch语句后的代码

在这里插入图片描述

catch(...)捕获任意类型异常

#include<iostream>
using namespace std;

//除法函数
double division(double a, double b)
{
	if (b == 0)
		throw "Divide by zero error";

	return a / b;
}
//除法函数调用者
void Func()
{
	while (1)
	{
		double a, b;
		cin >> a >> b;
		try
		{
			cout << division(a, b) << endl;
		}
		catch (const char*str)
		{
			cout << str << endl;
			throw 6;//抛出另外的异常
		}

		cout << "void Func()" << endl;
	}
}
//主函数
int main()
{
	//捕获
	try
	{
		Func();
	}
	catch (const char*str)
	{
		cout << str << endl;
	}
	catch (...)//捕获任意类型的异常
	{
		cout << "捕获未知异常" << endl;
	}


	return 0;
}

在这里插入图片描述

3. 异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或者没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄露(内存泄露)
  • C++中异常经常导致资源泄露的问题,因为throw抛出异常,会直接查找catch捕捉语句,导致后续的代码无法执行。在互斥锁中,lock和unlock之间抛出异常就会导致死锁

四. 多态在异常中的应用

多态是使用基类指针或者基类引用接收不同对象,会有不同的效果。
因为异常存在多种形式,多种错误信息,所以可以提供一个基类,其派生类再添加各自的错误信息。

比如,我们定义一个Exception基类

class Exception
{
public:
	//构造函数
	Exception(string &errMes,int errId)
		:_errMes(errMes)
		,_errId(errId)
	{}

	//显示错误信息的虚函数
	virtual string what()const
	{
		return _errMes;
	}

//权限需要设置成保护的
//不然派生类无法访问
protected:
	string _errMes;
	int _errId;
};

我们定义what函数,获取异常的错误信息,派生类通过重写what虚函数,或者添加成员变量来满足不同的需求

比如在项目中,我们可能会用到数据库,网络。那么在不同部分抛出的异常信息不同,就可以使用派生类

//数据库异常
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 += _errMes;
		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 += _errMes;
		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 += _errMes;

		return str;
	}
private:
	const string _type;
};

在main函数中,不管调用哪个功能,抛出的异常都可以用const Exception&捕获,调用what显示异常信息。

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

C++也有提供异常类——excepion

在这里插入图片描述

exception就是异常类的基类,提供虚函数what

在这里插入图片描述

也实现了很多派生类
像我们使用的new关键字,其实是调用了operator new

在这里插入图片描述

如果失败,会抛bad_alloc这个异常

下图是C++提供的异常类

在这里插入图片描述
在这里插入图片描述

六. 异常规范

  1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。可以在函数的后面接throw(类型),列出这个函数可能抛出的所有异常类型
  2. 函数后面接throw(),表示函数不抛异常
  3. 若无异常接口声明,则此函数可以抛任何类型的异常

比如:

在这里插入图片描述
第一个**throw(std::bad_alloc)表明可能会抛std::bad_alloc这个异常
第二个和第三个
throw ()**表明不会抛异常

但是因为这个不是强求的,所以很多人并不会遵循这个异常规范。其次,即使throw()标识不会抛异常,但是仍然在其中抛异常也并不会报错。抛出了未标识的异常也不会报错

所以C++11为了更加的规范,就提供了noexcept在这里插入图片描述

比如线程的构造函数
一个函数如果明确不抛异常,可以加noexcept。但是如果加了noexcept仍然抛异常,也不会直接报错或者终止程序,而是会有警告。
如果对加了noexcept的函数进行try,catch捕获,则会终止程序
在这里插入图片描述
在这里插入图片描述

可能会抛异常,就不加noexcept,C++98的异常规范看个人

七. 异常的优缺点

C++异常的优点

  1. 异常不同于C语言的错误码,异常可以接收对象,可以包含更多信息,可以更清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug
  2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误。而异常可以直接在main函数中捕获,达到直接跳转到最外层
  3. 很多第三方库都使用了一场,比如boost,gtest,gmock等等常用的库,如果要使用这些库,就需要捕获异常
  4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便用错误码的方式处理(虽然不建议构造函数抛异常)。返回值如果是模板,也无法返回错误码

C++异常的缺点

  1. 异常会导致程序的执行流乱跳,并且很混乱。这会使得调试分析变得困难
  2. C++没有垃圾回收机制,资源需要自己管理。使用异常可能导致内存泄露,死锁等异常完全问题。
  3. C++标准库的异常体系定义得不太好,导致大家各自定义各自的异常体系。

异常尽量规范使用,随意抛异常,外层捕获的人苦不堪言。所以异常规范有两点:一. 抛出异常类型都继承一个基类。二. 函数是否抛异常,抛什么异常,可以使用noexcept和throw(...)标识

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

IDEA快捷键及模版配置

一、快捷键 1、常用的快捷键 删除当前行, 默认是 ctrl Y 自己配置 ctrl d (搜delete)复制当前行, 自己配置 ctrl alt 向下光标 (搜duplicate)补全代码 alt /添加注释和取消注释 ctrl / 【第一次是添加注释&#xff0c;第二次是取消注释】导入该行需要的类 先配置 auto …

S3C2440点亮LED(裸机开发)

文章目录 前言一、环境介绍一、GPIO介绍二、点亮开发板的LED1.预备动作2.led代码 总结 前言 本期和大家主要分享的是使用S3C2440开发板点亮一个LED灯&#xff0c;可能大家拿到开发板之后做的第一件事情都是点灯&#xff0c;这是为什么呢&#xff1f;因为点灯这件事情不仅能够检…

TCP三次握手调优

SYN: Synchronize Sequence Numbers。同步序列号 服务端的优化 当服务器收到 SYN 报文后&#xff0c;服务器会立刻回复 SYNACK 报文&#xff0c;既确认了客户端的序列号&#xff0c;也把自己的序列号发给了对方。此时&#xff0c;服务器端出现了新连接&#xff0c;状态是 SYN…

MySQL系列之行转列,列转行

MySQL系列之行转列&#xff0c;列转行 之前业务出现了需要行转列的场景&#xff0c;记录一下 SQL中AVG、COUNT、SUM、MAX等聚合函数对NULL值的处理 Mysql Max、 Where和 Group By 三个关键字同时使用 执行顺序 MySql 行转列的玩法 &#xff0c;实战案例教学&#xff08;MAX…

Eolink Apikit,0 代码可拖拽的自动化测试神器

目录 一、从测试到可拖拽的自动化测试二、0 代码&#xff0c;图形化&#xff0c;好用到飞起通过图形化操作、拖拽的方式搭建测试流程 三、Eolink Apikit&#xff0c;一站式 API 研发协作平台1、多协议支持2、多种数据重用3、报告、分析、告警4、支持持续集成和部署 四、Apikit …

QT基础入门之文件操作

一、概述 Qt 作为一个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象&#xff0c;这些设备具有读写字节块的能力。下面是 I/O 设备的类图&#xff1a; QIODevice&#xff1a;所有I/O设备类的父类&#xff0c;提供字节块读写的通…

Sentinel流量规则模块(降级)

sentinel的常见页面的简介 流控是对外部来的大流量进行控制&#xff0c;熔断降级的视角是对内部问题进行处理。 Sentinel降级会在调用链路中某个资源出现不稳定状态时&#xff08;例如调用超时或异常比例升高&#xff09;&#xff0c;对这个资源的调用进行限制&#xff0c;…

【JDBC系列】- jdbc的概念以及与数据库的交互流程

【JDBC系列】- jdbc的概念以及与数据库的交互流程 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享学习…

oc UITableView 列表

// // ViewController.m // OcDemoTest // // Created by Mac on 2023/7/14. //#import "ViewController.h" // 添加协议 interface ViewController ()<UITableViewDataSource> property (weak, nonatomic) IBOutlet UITableView *tableView;endimplementat…

Node中的的util.promisify()方法的介绍和基本实现

异步编程解决方案 我们知道&#xff0c;在JS中实现异步编程主要是通过以下几种方案&#xff1a; 回调函数&#xff1a;也是在ES6之前用的最多的方式&#xff0c;缺点是容易造成callback hell&#xff0c;可读性很差观察者模式&#xff1a;在NodeJS中的很多模块都继承了EventE…

二分搜索树节点删除

本小节介绍二分搜索树节点的删除之前&#xff0c;先介绍如何查找最小值和最大值&#xff0c;以及删除最小值和最大值。 以最小值为例&#xff08;最大值同理&#xff09;&#xff1a; 查找最小 key 值代码逻辑&#xff0c;往左子节点递归查找下去&#xff1a; ... // 返回以…

3.4 Bootstrap 按钮下拉菜单

文章目录 Bootstrap 按钮下拉菜单分割的按钮下拉菜单按钮下拉菜单的大小按钮上拉菜单 Bootstrap 按钮下拉菜单 本章将讲解如何使用 Bootstrap class 向按钮添加下拉菜单。如需向按钮添加下拉菜单&#xff0c;只需要简单地在在一个 .btn-group 中放置按钮和下拉菜单即可。您也可…

❤️创意网页:如何用HTML制作菜单栏?制作好看的菜单栏样式网页

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

ChatGPT流量下降?原因竟是学生放暑假,秋季或将回暖

ChatGPT是一款由OpenAI开发的人工智能聊天机器人&#xff0c;它能够进行自然语言对话&#xff0c;并支持用户在写作业、进行研究等方面提供帮助。许多人认为它是历史上增长最快的科技产品之一&#xff0c;但近期却观察到其流量下降的现象。 根据Similarweb和其他机构在本月初发…

el-image-viewer图片预览组件使用

只需要安装了element-plus即可使用 <template><div class"preview-box"><!-- 第一种: 使用el-image - 通过点击小图, 然后预览大图, 这是官方文档提供的方法 --><el-image :preview-src-list"[/api/file/getImage/202307/3178033358P0KiZ…

基于Javaweb实现ATM机系统开发实战(十三)交易记录查看实现

老规矩&#xff0c;先看前端传递怎样的数据&#xff0c;已经把要展示数据的变量名都改了&#xff1a; <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%> <% taglib prefix"c" uri"…

7、网络层(地址管理和路由选择)IP31

网络层&#xff1a;负责地址管理和路由选择 IP协议&#xff0c;路由器 一、IP协议 4位协议版本号&#xff1a;4/6 -ipv4、ipv6 4位报头长度&#xff1a;以4B为单位描述报头大小&#xff0c;IP报头最大60B最小20B 8位服务类型&#xff1a;3位优先权字段弃用&#xff0c;1位保留…

Circular lollipop | 哇咔咔!!!环形棒棒糖图好吃又好玩!~

1写在前面 今天不想废话了&#xff0c;直接看图吧。&#x1f447; 复现代码step by step&#xff0c;自己看吧。&#x1f92a; 2用到的包 rm(list ls())library(tidyverse)library(ggtext)library(patchwork) 3示例数据 df_pw <- read.csv("./passwords.csv",row…

浅析 Io 处理

文件流&#xff1a; 在Java 中&#xff0c;文件流负责操作文件&#xff0c;包括读取和写入&#xff1b; FileInputStream // 文件的字节输入流&#xff1b; FileOutputStream // 文件的字节输出流&#xff1b; FileReader // 文件的字符输入流&#xff1b; FileWriter // 文…

Python基于百度智能云平台股票资讯情感分析

Python基于百度智能云平台股票资讯情感分析 全部代码和数据地址如下&#xff1a;Python基于百度智能云平台股票资讯情感分析 本文章详细内容如下&#xff1a; 文章目录 Python基于百度智能云平台股票资讯情感分析导入相应的包1.引入库2.设置账户秘钥3.导入数据4.数据合并5.百度…