C++异常学习

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

传统的错误处理机制:

  1. 终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序。
  2. 返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通
    过把错误码放到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的处理代码。
  2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,
    所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似
    于函数的传值返回)
  4. catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么。
  5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,
    使用基类捕获,这个在实际中非常实用,我们后面会详细讲解这个。

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

#include<iostream>
using namespace std;
int divs(int m, int n)
{
	if (n == 0)
	{
		throw"被除数不能为0";
	}
	return m / n;
}

void func1(int m ,int n )
{
	divs(m, n);
}

void func2(int m, int n)
{
	func1(m, n);
}

void func3(int m, int n)
{
	func2(m, n);
}

int main()
{

	try
	{
		int m, n;
		cin >> m >> n;
		func3(m, n);
	}
	catch (const char* s)
	{
		cout << s << endl;
	}
	catch (...)
	{
		cout << 11 << endl;
	}


	return 0;
}

在这里插入图片描述
很高兴,这里学到了一个新的知识点,之前学的太马虎了

#include<iostream>
#include<string>
using namespace std;
int divs(int m, int n)
{
	if (n == 0)
	{
		throw"被除数不能为0";
	}
	return m / n;
}

void func1(int m ,int n )
{
	divs(m, n);
}

void func2(int m, int n)
{
	func1(m, n);
}

void func3(int m, int n)
{
	func2(m, n);
}

int main()
{

	try
	{
		int m, n;
		cin >> m >> n;
		func3(m, n);
	}
	catch (string s)
	{
		cout << s << endl;
	}
	catch (...)
	{
		cout << 11 << endl;
	}


	return 0;
}

用下面的代捕获就不行,就不知道原因,我throw不就是一个字符串吗,为什么不能接受。后来发现,throw抛出的是字符串常量,跟string类型确实不一样。我们创建一个string是怎么样的啊?
string s =“ssss”;
其实这里就是把字符串常量变成string类型。
下面是chat老师的解释

你的代码中使用 throw “被除数不能为0”; 时,抛出的异常是一个字符串常量,其类型是 const char* 而不是 std::string。因此,在 catch 块中,你应该捕获 const char* 类型的异常而不是 std::string。

如果你想要用 std::string 来捕获这个异常,你应该在抛出异常时使用 std::string 类型,像这样:throw std::string(“被除数不能为0”);。

字符串常量跟string有什么区别呢
ChatGPT
字符串常量和 std::string 类型在 C++ 中有几个主要的区别,这些区别涉及它们的类型定义、内存管理、使用方式和功能。这里是一些关键区别:

  1. 类型和存储
    字符串常量(如 “hello”)是由编译器在编译时处理的,它们实际上是 const char[] 类型,即一个以 null (‘\0’) 字符结束的字符数组。这些字符串存储在程序的只读数据段中,通常不能被修改。
    std::string 是 C++ 标准库中定义的一个类,用于处理文本数据。它封装了动态数组的管理,可以容纳可变长度的字符串,并提供了广泛的成员函数来进行字符串操作。std::string 对象通常存储在堆上,或者在小字符串优化 (SSO) 情况下在栈上。
  2. 内存管理
    字符串常量 不需要用户进行内存管理,它们由编译器管理,并且通常不能被修改。
    std::string 管理它自己的内存,自动进行内存分配和释放。它支持动态改变大小,可以在运行时添加、修改或删除字符。
  3. 功能和方法
    字符串常量 基本上只能用作不变的文本。要执行任何复杂的操作(如拼接、搜索或替换),你需要使用 C 风格的字符串处理函数(如 strlen, strcpy, strcat 等)。
    std::string 提供了许多成员函数和运算符重载,使得字符串处理更加直接和安全。例如,你可以使用 + 运算符来拼接字符串,使用 [] 运算符来访问特定位置的字符,以及使用 .length() 或 .size() 来获取字符串的长度等。
  4. 安全性和方便性
    使用 字符串常量 可能会涉及到指针操作,这在不正确使用的情况下可能导致程序崩溃或安全问题(例如缓冲区溢出)。
    std::string 设计时考虑到安全性和方便性,自动处理内存管理,减少了缓冲区溢出的风险,并简化了代码。

查看异常捕获机制

#define  _CRT_SECURE_NO_WARNINGS

#include<iostream>
using namespace std;
int divs(int m, int n)
{
	if (n == 0)
	{
		throw"被除数不能为0";
	}
	return m / n;
}

void func1(int m, int n)
{
	divs(m, n);
}

void func2(int m, int n)
{
	try
	{
		func1(m, n);
	}
	catch (const char* s)
	{
		cout << __LINE__ << endl;
	}
	cout << 1111 << endl;
	
}

void func3(int m, int n)
{
	func2(m, n);
}

int main()
{

	try
	{
		int m, n;
		cin >> m >> n;
		func3(m, n);
	}
	catch (const char* s)
	{
		cout << __LINE__ << endl;
		cout << s << endl;
	}
	catch (...)
	{
		cout << 11 << endl;
	}


	return 0;
}

在这里插入图片描述
自定义异常类
在这里插入图片描述

#define  _CRT_SECURE_NO_WARNINGS
#include<string>
#include <time.h>
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;
};


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 HttpServer()
{
	// ...
	srand(time(0));
	if (rand() % 3 == 0)
	{
		throw HttpServerException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpServerException("权限不足", 101, "post");
	}
	CacheMgr();
}
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();
}

int main()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));
		try {
			HttpServer();
		}
		catch (const Exception& e) // 这里捕获父类对象就可以
		{
			// 多态
			std::cout << e.what() << std::endl;
		}
		catch (...)
		{
			std::cout << "Unkown Exception" << std::endl;
		}
	}
	return 0;
}

异常规范

  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;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac 部署 llamafile 大语言模型LLM

文章目录 Github官网本地部署 llamafile 是一种可在你自己的电脑上运行的可执行大型语言模型&#xff08;LLM&#xff09;&#xff0c;它包含了给定的开放 LLM 的权重&#xff0c;以及运行该模型所需的一切。让人惊喜的是&#xff0c;你无需进行任何安装或配置。 Github https…

scala---基础核心知识(变量定义,数据类型,流程控制,方法定义,函数定义)

一、什么是scala Scala 是一种多范式的编程语言&#xff0c;其设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台&#xff08;Java虚拟机&#xff09;&#xff0c;并兼容现有的Java程序。 二、为什么要学习scala 1、优雅 2、速度快 3、能融合到hado…

突破深度模型线上耗时瓶颈,我们做了什么?

广告投放是深度模型应用较为普遍的场景之一&#xff0c;虽然深度模型能够提升业务效果&#xff0c;但往往也会付出更加高额的耗时开销。滴滴现今 DSP&#xff08;Demand-Side Platform&#xff09; 业务场景中&#xff0c;耗时问题已然成为限制模型发挥的魔咒&#xff0c;为了打…

选课成绩管理系统

文章目录 员工管理系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目&#xff08;9.9&#xffe5;&#xff09; 员工管理系统 一、项目演示 课程管理系统 二、项目介绍 基于springbootvue的前后端分离选课成绩管理系统 该系统可做课程管理…

nginx使用http2,并配置ssl证书

** nginx使用http2&#xff0c;并配置ssl证书 ** 想要使用http2&#xff0c;需要在安装nginx时安装http2模块和ssl模块 前置条件nginx版本需要在1.9.5以上 #解压nginx包 tar -zxvf nginx-1.18.0.tar.gz #进入nginx目录 cd nginx-1.18.0 #执行 ./configure --prefix/usr/lo…

使用 object-fit 属性完美过渡图片

object-fit 属性指定元素的内容应该如何去适应指定容器的高度与宽度&#xff0c; 一般用于 img 和 video 标签&#xff0c;一般可以对这些元素进行保留原始比例的剪切、缩放或者直接进行拉伸等 在我们工作中&#xff0c;经常会遇到附件上传&#xff0c;然后展示多张图片的&…

数字化应用标杆 | 利驰软件助力博方电气提效高达99.8%

数字制造应用标杆合作——利驰✍博方 近日&#xff0c;利驰数字科技&#xff08;苏州&#xff09;有限公司&#xff08;简称 利驰软件&#xff09;与河南博方电气有限公司&#xff08;简称 博方电气&#xff09;成功签订了数字制造应用标杆合作协议&#xff0c;这一里程碑式的合…

Zynq学习笔记--数字视频帧以及同步信号

目录 1. 介绍 2. 重要概念 3. 仿真测试 4. 总结 1. 介绍 Zynq芯片&#xff0c;作为一款集成了高性能FPGA和ARM处理器的系统级芯片(SoC)&#xff0c;为视频处理提供了强大的硬件支持。该芯片内置的丰富视频方面的IP模块&#xff0c;使得从事视频处理项目的开发者能够高效、…

Revo Uninstaller Pro:让卸载不再留下遗憾的专业工具

在数字化时代&#xff0c;我们的电脑中充满了各式各样的软件。然而&#xff0c;当我们想要卸载某些不再需要的程序时&#xff0c;往往会发现卸载并不如安装那般简单。残留的注册表项、碎片化的文件以及顽固的后台进程&#xff0c;这些都可能成为卸载的绊脚石。幸运的是&#xf…

将数字状态码在后台转换为中文状态

这是我们的实体类 可以看出我们的状态status是2如果返回到前端我们根本不知道2代表的是什么&#xff0c;所以我们需要再这里将数字转换成能看懂的中文状态&#xff0c;首先我们创建一个枚举类 先将我们状态码所对应的中文状态枚举出来&#xff0c;然后创建一个静态方法&#…

P1737 [NOI2016] 旷野大计算 题解

题目大意&#xff1a;只允许使用加、取反&#xff08;添负号&#xff09;、偏移&#xff08;加减一个常数&#xff09;、左右移位&#xff08;乘或除以 22 的非负整数次幂&#xff09;和神奇的 &#xfffd;(&#xfffd;)S(x) 函数来进行编程&#xff0c;造一台计算机&#xf…

RocketMQ 存储机制浅析

RocketMQ 是一个典型的发布订阅系统&#xff0c;通过 Broker 节点中转和持久化数据、解耦上下游。Broker 是真实存储数据的节点&#xff0c;由多个水平部署但不一定完全对等的副本组构成&#xff0c;单个副本组的不同节点的数据会达到最终一致。RocketMQ 优异的性能表现&#x…

UE4_动画基础_根运动Root Motion

学习笔记&#xff0c;仅供参考&#xff01; 在游戏动画中&#xff0c;角色的碰撞胶囊体&#xff08;或其他形状&#xff09;通常由控制器驱动通过场景。然后来自该胶囊体的数据用于驱动动画。例如&#xff0c;如果胶囊体在向前移动&#xff0c;系统就会知道在角色上播放一个跑步…

华为“天才少年”4万字演讲:现在的AI技术要么无趣,要么无用

近期&#xff0c;一篇4万字的演讲风靡于国内人工智能&#xff08;AI&#xff09;学术圈。 原华为“天才少年”、Logenic AI公司联合创始人李博杰博士&#xff0c;日前发表了一篇关于AI Agent思考的文章&#xff0c;题为“AI Agent 应该更有趣还是更有用”。 李博杰在这篇文章…

存储过程的创建和调用及删除

目录 存储过程 存储过程的创建 存储过程的调用及删除 在 SQL Plus 中调用存储过程 在 PL/SQL 块中调用存储过程 存储过程的删除 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 存储过程 存储过程是一种命名的 PL/S…

使用Mybatisforeach循环添加字段和值失败问题记录

问题描述: 由于数据表字段非常多,使用foreach循环,key为数据库字段,value为要添加的值.字段中含有小数点的无法正常添加数据 问题展示: 断点展示有值 日志展示获取不到值 sql如下: <insert id"dataMergeInsert" parameterType"java.util.List">IN…

gemini国内怎么用

gemini国内怎么用 Google Gemini 作为一个尚处于研发阶段的大型语言模型&#xff0c;其具体功能和性能尚未公开&#xff0c;因此无法对其好用程度做出明确评价。 然而&#xff0c;基于 Google 在人工智能领域的领先地位和技术实力&#xff0c;我们可以对其潜力进行一些推测&a…

科技云报道:“老三样”不管用了,网络安全要靠啥?

科技云报道原创。 从安全的视角看&#xff0c;网络空间充斥着病毒、黑客、漏洞。在过去&#xff0c;企业习惯用“老三样”——防火墙、IDS、杀毒软件来搞定安全。 如果将网络空间比喻成一个大厦&#xff0c;那么防火墙相当于门锁&#xff0c;用于隔离内外网或不同安全域&…

【MySQL 数据宝典】【内存结构】- 004 自适应哈希索引

自适应哈希索引 https://developer.aliyun.com/article/1230086 什么是自适应哈希索引&#xff1f; 自适应哈希索引是MySQL InnoDB存储引擎中的一种索引结构&#xff0c;用于加速查询。它根据查询模式和数据分布动态地调整自身的大小&#xff0c;以提高性能。 上图就是通过…
最新文章