【关于C++中----异常】

news2024/11/25 6:33:00

文章目录

  • 一、C语言中处理错误的方式
  • 二、C++异常概念
  • 三、异常的使用
    • 3.1 异常的抛出和捕获
    • 3.2 异常的重新抛出
    • 3.3 异常安全
    • 3.4 异常规范
  • 四、自定义异常体系
  • 五、C++标准库的异常体系
  • 六、异常的优缺点


一、C语言中处理错误的方式

C语言中常见的错误类型包括:语法错误、逻辑错误、运行时错误和系统错误。程序员需要了解如何识别和处理这些错误,以确保程序的正确性和健壮性。

  1. 终止程序
    如assert,当发生错误时,直接终止程序
    这种做法非常暴力,有时候可能只是一个无关痛痒的小错误,就会导致后面的代码都无法运行。
  2. 返回错误码
    如果函数体里发生错误时,将错误码返回给coder,需要去查找对应的错误,系统的库函数接口就是通过把错误码放到errno中,表示错误。需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误。

实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。


二、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.1 异常的抛出和捕获

异常的抛出和匹配原则

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。

如下:

double Division(int a, int b)
{
	// 当b == 0时抛出异常
	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()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (int errmsg) 
	{
			cout << errmsg << endl;
	}
	return 0;
}

结果如下:
在这里插入图片描述

在有多个catch的情况下,只会进入类型匹配的那一个。而如果,没有类型匹配的catch,就会直接终止程序。 如下:
在这里插入图片描述

  1. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。

将Func函数改为如下内容:

void Func()
{
	try
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
	}
	catch (const char* errmsg)
	{
		cout << "the first" << errmsg << endl;
	}
}

再次运行,结果如下:
在这里插入图片描述
可见,确实进入了调用链中未匹配,且位置最近的那一个。

注意,在同一个作用域中,不能出现多个同一类型的catch,而在不同作用域中可以。

  1. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)。
  2. catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么。

如下:
在这里插入图片描述

  1. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获。

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

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

3.2 异常的重新抛出

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

而在实际的一些大型的项目中,往往需要在同一个地方抛出所有的异常,并记录日志。那么可以采用下面这个方法:
在这里插入图片描述
在catch中继续抛出异常,让上一层调用链中的catch捕获。(这种方法比较low,不建议使用,可以使用智能指针来实现)

3.3 异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用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;

四、自定义异常体系

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

下面是一个简单的模拟项目异常抛出体系:

// 服务器开发中通常使用的异常继承体系
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{}
	virtual string what() const
	{
		return _errmsg;
	}
protected:
	string _errmsg;
	int _id;
};
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;
	}
};
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(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)
	{
		this_thread::sleep_for(chrono::seconds(1));
		try {
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			cout << e.what() << endl;
		}
		catch (...)
		{
			cout << "Unkown Exception" << endl;
		}
	}
	return 0;
}

模拟结果如下:
在这里插入图片描述


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

C++ 提供了一系列标准的异常,定义在 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:
在这里插入图片描述
下面是其中的描述:
在这里插入图片描述

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


六、异常的优缺点

C++异常的优点:

  1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。
  2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误
  3. 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常。
  4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

C++异常的缺点:

  1. 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。
  2. 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
  3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高。
  4. C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
  5. 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化。

总结:异常总体而言,利大于弊,所以工程中我们还是鼓励使用异常的。另外OO的语言基本都是用异常处理错误,这也可以看出这是大势所趋。


本篇完,青山不改,绿水长流!

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

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

相关文章

访问 virtualbox中的mysql

在 mysql.user 中存储这用户可访问的的host地址 select user,host from user;修改访问权限 可以使用sql语句 use mysql; mysql>update user set host % where user root; flush privileges或者使用mysql的权限语句 use mysql; Grant all on *.* to root% identified b…

深入浅出C++ ——异常

文章目录 一、C语言传统的处理错误的方式二、C异常概念三、异常的使用异常的抛出和捕获异常的重新抛出异常安全异常规范 四、自定义异常体系无、C标准库的异常体系六、异常的优缺点 一、C语言传统的处理错误的方式 C语言传统的处理错误的机制&#xff1a; 终止程序&#xff0…

docker打包部署spring boot应用(mysql+jar+Nginx)

文章目录 一、基本准备二、mysql部署二、jar部署三、Nginx部署 一、基本准备 小唐拿的就是之前放置在我们服务器上的应用进行部署&#xff0c;主要就是mysql和jar还有Vue的部署。 目前已经有的是jar、已经打包好的vue 项目参考&#xff1a;小破站数据大屏可视化&#xff08;…

【IPv6】IPv6有无状态地址分配及IPv6路由

IPv6有无状态区分 有状态可控、可管理。有IP地址管理者&#xff0c;能够识别客户端。根据不同客户端分配对应v6地址&#xff0c;客户端和服务器存在租期及续约。无状态无控、难管理。无IP地址管理者&#xff0c;没人识别客户端。客户端根据网关发送的相同的RA报文内容&#xf…

path/to/sdkmanager --install “cmdline-tools;latest“

执行flutter doctor时、报错Android Studio的命令行工具没有安装& 以及 android-licenses没有同意 其中提示错误语句如标题: path/to/sdkmanager --install "cmdline-tools;latest"之类的, 因为同意条款的时候,日志太多,所以把报错覆盖了.没有截图. 解决方法: …

一个简单的servlet+Jsp+MySQL/Oracle程序

一个简单的servletJspMySQL/oracle程序 1. 创建项目 使用 IDEA 创建一个 Maven 项目. 1.1、File -> New Project Name:javaservlet4 Location:选择要存放的路径 Language:Java Build system:Maven 点击Create按钮 ​​​​​​​1.2、Pom.xml配置 <dependencies…

整数在内存中的存储:原码、反码、补码 大小端字节序

本篇博客会讲解整数在内存中的存储形式&#xff0c;以及整数二进制的3种表示形式&#xff1a;原码、反码、补码&#xff0c;还有大小端的相关知识点。相信读完本篇博客&#xff0c;大家对内存的了解会上一个台阶。 注意&#xff1a;本篇博客讨论的是整数在内存中的存储&#x…

【五一创作】【数学建模】matlab的常用函数运用(1)

文章目录 1. matlab基本常识2. 常用输入输出函数2.1 输出函数2.2 拼接函数&#xff08;字符串的合并&#xff09;2.3 输入函数 3. 求和函数3.1 向量求和3.2 矩阵求和 4. 提取矩阵元素4.1 取第x行第y列的元素4.2 取指定行或列的所有元素4.3 取指定某些行的所有元素 1. matlab基本…

MongoDB聚合操作

文章目录 一、单一聚合二、聚合管道2.1 $match / $project / $count2.2 $limit / $skip / $sort2.3 $group 分组查询2.4 $unwind 展开数组2.5 $lookup 左外连接2.6 $bucket 存储桶 三、MapReduce 提示&#xff1a;以下是本篇文章正文内容&#xff0c;MongoDB 系列学习将会持续更…

域名解析出现错误,该如何解决?

域名作为网络地址&#xff0c;是我们访问网站的必经之路&#xff0c;域名解析就是把你的域名解析成一个ip地址&#xff0c;在使用的过程中遇到域名解析文件异常也是常有的事。如果域名解析出现错误&#xff0c;该怎么解决呢&#xff1f; 一、打开网页时&#xff0c;显示域名解析…

Redis主从复制、哨兵实战

环境&#xff1a;linux centos7.x &#xff0c;虚拟机3台 版本&#xff1a;redis-6.2.6 1.下载安转redis 下载地址 wget https://download.redis.io/releases/redis-6.2.6.tar.gz解压 tar -zxvf redis-6.2.6.tar.gz移动目录 mv redis-6.2.6 /usr/local/redis编译 cd /usr/…

【GORM框架】ORM介绍、GORM简单连接和高级配置详解

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;数据结构、Go&#xff0c;Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: GORM框架学习 近期目标&#xff1a;写好专栏的每一篇文章 目录 一、简介…

配准带尺度点云的方法汇总

如果点集之间不存在缩放关系时(即尺度相同时), 可以用经典ICP( Iterative Closest Point )方法求解得到旋转矩阵R和平移向量t来进行点集对齐。 如果存在缩放关系时&#xff0c;首先估计出点集S1和S2之间的缩放倍数s, 我们就可以利用ICP算法求解。 一、尺度因子s是两个点集中线…

HashSet底层原理

特点&#xff1a;无序、不可重复 LinkedHashSet 有序、可重复&#xff08;底层通过双向链表的方式记录元素的存储顺序&#xff09; HashSet底层数据结构是哈希表 jdk1.8之前&#xff1a;哈希表组成&#xff1a;数组 链表 jdk1.8之后&#xff1a; 数组 链表 红黑树 存储…

Flutter 组件抽取:日期(DatePicker)、时间(TimePicker)弹窗选择器【仿照】

简介 仿照《Flutter 仿ios自定义一个DatePicker》实行的日期弹窗选择器&#xff08;DatePicker&#xff09;、时间弹窗选择器&#xff08;TimePicker&#xff09; 效果 范例 class _TestPageState extends State<TestPage> {overridevoid initState() {super.initStat…

Java笔记_15(集合三)

Java笔记_15 一、创建不可变集合1.1、创建不可变集合的应用场景1.2、创建不可变集合的书写格式 二、Stream流2.1、体验Stream流2.2、Stream流的思想和获取Stream流2.3、Stream流的中间方法2.4、Stream流的终结方法2.5、收集方法collect 一、创建不可变集合 不可变集合&#xf…

山东省2023年春季高考技能测试电子技术类专业试题

注意事项 1.本试题为样题&#xff0c;实际测试试题与样题基本一致&#xff0c;不同场次试题电路装配要求会有变化&#xff0c;请考生仔细审题。 2.严禁考生私自送电&#xff0c;严禁带电操作&#xff08;通电调试除外&#xff09;。 3.考生要服从监考人员安排&#xff0c;遵…

【python入门篇】安装python教程

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

【GPT】AutoGPT 安装使用完全教程

欢迎关注【youcans的GPT学习笔记】原创作品&#xff0c;火热更新中** 【GPT】AutoGPT 安装使用完全教程 【GPT】AutoGPT 安装使用完全教程1. AutoGPT 介绍1.1 AutoGPT 简介1.2 AutoGPT 的工作流程 2. 下载 AutoGPT 项目源码2.1 GitHub 下载项目源码2.2 网页下载稳定版源码 3. A…

数据结构(六)—— 二叉树(1)基础

文章目录 前言一、二叉树1.1 满二叉树1.2 完全二叉树1.3 二叉搜索树1.4 平衡二叉搜索树 二、二叉树的遍历2.1 深度优先遍历&#xff08;DFS&#xff09;2.2 广度优先遍历&#xff08;BFS&#xff09; 三、二叉树的代码定义 前言 提示&#xff1a;这里可以添加本文要记录的大概…