c++——异常

C语言常用的错误处理机制是返回错误码和assert.

返回错误码:用不同数字代表不同的错误,当程序发生错误时会把全局变量errno的值修改为相应数字,需要程序员自己通过数字去查找对应的错误。

assert:发生错误就暴力终止程序。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

c++异常

c++通过抛出和捕获来处理异常。

throw: 当问题出现时,通过使用 "throw 关键字+对象",程序会抛出一个异常,这个对象中包含异常信息。

try:如果一段代码可能抛出异常,就将其放入try块,try块中的代码叫做保护代码。(在try模块内部创建的变量的作用域只在try内部)

catch : try语句块后面需要跟着一个或多个 catch 块,用于捕获try中抛出的异常。

简单使用try/catch的语法及实例如下所示:


接下来说明try/catch的机制:

1.try和catch是一个整体,因此你不可以在try和catch两个语句块以及catch和catch两个语句块中间书写代码。

2.一但在某句代码抛出异常,就会直接跳到匹配的catch语句里面,中间的代码不予以执行,比如上面的例子中就没有执行func2()。

3.try/catch是这样做的:

如上,throw之后会在作用域1,2,3...main依此逐层找包含该throw的 最近的try语句块,如果直到main函数还没有找到try则会报错。

相反,如果在此过程中找到了最近的 try语句块,则再去从该try对应的一个或者多个catch语句块中找参数与抛出对象类型相同的catch语句块(这里的类型匹配是强匹配,也就是说,int和size_t等貌似可以匹配的也算不匹配),并跳到该catch语句块里面执行。如果没有找到匹配的catch语句块,则继续依此逐层找次近的try语句块。

实际上,上面的作用域套娃就是函数的嵌套调用,沿着函数调用链查找匹配的 catch子句的过程称为栈展开(因为函数调用存在于栈区,而查找的过程就是查找,去下一层,查找,去下一层......)


接下来,可以细说catch的问题

如上,catch有多个,但是这是否太赘余了?事实上,我们一般有规范且简便的自定义异常体系:

我上面说throw对象的类型与catch参数的类型必须是强匹配,但这儿有个例外,就是父类的引用可以接受不同的子类(类似于多态)

因此,我们把错误信息包装在类里面,如果说这个错误有它特有的信息,我们就可以抛出一个子类(这个子类里面添加的新的变量,里面存储特有信息),子类仍然可以被父类的引用接受。

效果如下:

这样,对于不同格式的错误,我们只需要写一个子类就好了,且方便管理。

(c++库也是给出了标准的错误类及它的派生类用于记录不同的错误,但是我们一般是自己写,可以自己在官网查看一下)

除此之外,为了防止上面说的直到main函数还没有匹配成功导致报错,一般在main函数中的try后面加上catch(...){cout<<"未知错误";}

catch(...){}可以捕获任意类型。

异常规范

// 这里表示这个函数会抛出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;

需要注意的是,noexcept是强制检查,违反了会使程序终止,而throw只会报警告。

异常安全

构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不 完整或没有完全初始化

析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内 存泄漏、句柄未关闭等)

C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄 漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII 我们智能指针这节进行讲解。

异常重新抛出

异常被捕获之后可以在catch里重新抛出,向更外层传递。

这里可以用两个例子来说明重新抛出

1.

这里如果func2抛出异常,会被main中的catch捕获。然而 func2()后面的代码没有执行,所以,arr1和arr2的空间没有被释放,造成内存泄漏。

这时候就可以用到异常的重新抛出:

先在中途捕获这个异常,然后释放动态资源,然后重新抛出,让main函数中的catch捕获。(只写一个throw表示捕获到什么就重新抛出什么)

难道这样就完美了吗?

事实上,new失败的话也会抛异常,如果创建arr2时抛出异常,那么arr1就不会被释放,所以在arr1处还要加一个重新捕获。

这样太麻烦,当然后续可以用智能指针处理

相关推荐
SilentSamsara9 分钟前
生成器完全指南:`yield` 与惰性求值的工程价值
linux·开发语言·python·算法·机器学习·青少年编程
jieyucx8 小时前
Go语言深度解剖:Map扩容机制全解析(增量扩容+等量扩容+渐进式迁移)
开发语言·后端·golang·map·扩容策略
顾温8 小时前
default——C#/C++
java·c++·c#
凉茶钱8 小时前
【c语言】动态内存管理:malloc,calloc,realloc,柔性数组
c语言·c++·vscode·柔性数组
脏脏a8 小时前
【C++模版】泛型编程:代码复用的终极利器
开发语言·c++·c++模版
island13148 小时前
【C++仿Muduo库#3】Server 服务器模块实现上
服务器·开发语言·c++
散峰而望8 小时前
【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
c语言·开发语言·数据结构·c++·算法·github
小龙报8 小时前
【C语言】内存里的 “数字变形记”:整数三码、大小端与浮点数存储真相
c语言·开发语言·c++·创业创新·学习方法·visual studio
深耕AI8 小时前
【VS Code避坑指南】点击Python图标提示“没有Python环境”,选择安装uv后这堆输出到底是什么意思?
开发语言·python·uv
刃神太酷啦8 小时前
扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)
java·c语言·javascript·数据结构·c++·算法·leetcode