📝前言:
这篇文章我们来讲讲C++异常:
🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏,Linux
文章目录
- 一,异常处理
- 1.1 基本语法
- 1.1 抛出异常
- 1.2 捕获异常
- 二,具体的找catch的栈展开过程
- 三,catch 的类型匹配
- 四, 异常的重新抛出
- 五,异常安全问题
- 六,异常的规范
- 七,标准异常
一,异常处理
异常处理是C++中处理运行时错误的重要机制,它允许程序在遇到错误时能够优雅地恢复或终止,而不是直接崩溃。
1.1 基本语法
C++异常处理使用三个关键字:try
, catch
, 和 throw
。
try {// 可能抛出异常的代码if (error_occurred) {throw exception_object;}
} catch (exception_type1& e) {// 处理exception_type1类型的异常
} catch (exception_type2& e) {// 处理exception_type2类型的异常
} catch (...) {// 处理所有其他类型的异常
}
1.1 抛出异常
使用throw
关键字抛出异常:
double divide(double a, double b) {if (b == 0.0) {throw "Division by zero!"; // 抛出字符串字面量}return a / b;
}
1.2 捕获异常
使用catch
块捕获并处理异常:
try {double result = divide(10.0, 0.0);
} catch (const char* msg) {cerr << "Error: " << msg << endl;
}
- 程序先执行
try
里面的语句,如果try
里面throw
了异常,throw
后⾯的语句将不再被执行。程序的执行从throw
位置跳到与之匹配的catch
模块 - 找
catch
的规则:找调用链中与该对象类型匹配且离抛出异常位置最近的那⼀个 - 从
throw
到catch
:1,沿着调用链的函数可能提早退出。2、沿着调用链创建的对象都将销毁 - 抛出异常对象后,会⽣成⼀个异常对象的拷⻉,因为抛出的异常对象可能是⼀个局部对象,所以会⽣成⼀个拷⻉对象,这个拷⻉的对象会在catch⼦句后销毁。(这⾥的处理类似于函数的传值返
回)
二,具体的找catch的栈展开过程
- 如果
throw
在try
里面,则找对应的catch
- 如果没找到 / 类型不匹配,则提前退出当前函数,到外层函数里面(函数调用链)找
- 如果找到
main
函数都没找到,则程序会调⽤标准库的terminate
函数终止程序【main
函数兜底】 - 如果这个过程中有一步找到了,则跳去执行
catch
,执行完以后,不会回到原来的位置,而是继续执行catch
后面的语句,相当于throw
改变了执行流程
三,catch 的类型匹配
除了严格的完全对应的类型匹配,以下几种也可以匹配:
- 允许从⾮常量向常量的类型转换,也就是权限缩⼩
- 允许数组转换成指向数组元素类型的指针,函数被转换成指向函数的指针
- 允许从派⽣类向基类类型的转换(实际中继承体系基本都是⽤这个⽅式设计)
catch(...)
:...
表示匹配任意类型(但是不知道异常错误是什么),main
函数的兜底就是这个
四, 异常的重新抛出
在catch块中可以重新抛出当前异常:
try {// ...
} catch (const std::exception& e) {// 部分处理throw; // 重新抛出相同的异常
}
- 重新抛出的是原始异常对象(不是拷贝),并且保留原始的异常类型和内容。
- 不会被同一个
catch
块再次捕获,而是会跳出当前try-catch
块,继续向外层传播,由外层的try-catch
块捕获。
五,异常安全问题
- 异常抛出后,后⾯的代码就不再执行,如果前面申请了资源,但是释放代码在
catch
后面,而catch
中又重新抛出了异常,就会没有释放,引发资源泄漏,产⽣安全性的问题。解决方法:1,捕获异常以后,要先释放资源,再重新抛出;2,用智能指针的RAII
方式解决这种问题是更好的。 - 其次,析构函数中,如果抛出异常也要谨慎处理,比如析构函数要释放10个资源,释放到第5个时抛
出异常,则也需要捕获处理,否则后⾯的5个资源就没释放,也资源泄漏了。
示例(解决方法1):
void process() {int* resource = new int(100); // 申请资源(假设是动态内存)try {some_operation_that_may_throw(); // 可能抛出异常} catch (...) {delete resource; // 手动释放资源throw; // 重新抛出异常}delete resource; // 正常情况下的释放
}
但是这种解决方法不好,后面用智能指针 +RAII
更好!
六,异常的规范
- 对于用户和编译器而言,预先知道某个函数是否会抛出异常有助于简化调用函数的代码
noexcept
关键字:函数参数列表后⾯加noexcept
表示不会抛出异常,啥都不加表示可能会抛出异常- 编译器并不会在编译时检查
noexcept
修饰的函数。即使里面还是有throw
- 但是,如果⼀个声明了
noexcept
的函数抛出了异常(里面有throw
,并且运行时抛出了),程序会调用terminate
终止程序 noexcept
还可以作为⼀个运算符去检测⼀个表达式是否会抛出异常,如果保证不会抛出异常就返回true
,可能会就返回false
示例(有noexcept
但是还是会throw
):
double Divide(int a, int b) noexcept
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
int main()
{try{int len, time;cin >> len >> time;cout << Divide(len, time) << endl;}catch (const char* errmsg){cout << errmsg << endl;}catch (...){cout << "Unkown Exception" << endl;}int i = 0;cout << noexcept(Divide(1, 2)) << endl;cout << noexcept(Divide(1, 0)) << endl;cout << noexcept(++i) << endl;return 0;
}
编译时通过,运行时,输入:1 0
,直接干没了
不带noexcept
:
double Divide(int a, int b)
输入2 2
运行结果:
七,标准异常
官方文档:https://legacy.cplusplus.com/reference/exception/exception/
- C++标准库也定义了⼀套自己的⼀套异常继承体系库,基类是
exception
,所以我们⽇常写程序,需要在主函数捕获exception
即可,要获取异常信息,调⽤what
函数,what
是⼀个虚函数,派生类可以重写。 - 标准异常头文件;
<stdexcept>
标准库常见异常类:
异常类 | 用途 |
---|---|
std::logic_error | 程序逻辑错误(如无效参数) |
std::runtime_error | 运行时错误(如文件不存在) |
std::bad_alloc | 内存分配失败(new 抛出) |
std::out_of_range | 越界访问(如 vector::at) |
示例:
int main() {try {// 尝试执行的代码块std::vector<int> v(1000000000); // 可能抛出 std::bad_allocthrow std::runtime_error("自定义运行时错误"); // 构造并抛出标准异常类:runtime_error异常,并且用"自定义运行时错误"构造它,把异常信息设置成这句话。后续.what()的时候,就可以获取异常信息}catch (const std::exception& e) {// 异常处理块std::cerr << "捕获异常: " << e.what() << std::endl;}return 0;
}
执行流程
-
try 块中的代码执行:
-
首先尝试创建一个超大
vector v(1000000000)
-
如果系统内存不足,会抛出
std::bad_alloc
异常 -
如果创建成功,继续执行下一行
-
-
然后执行
throw std::runtime_error("自定义运行时错误")
- 这行代码会主动抛出一个运行时错误异常
-
-
catch 块捕获异常:
-
无论抛出的是
std::bad_alloc
还是std::runtime_error
-
因为它们都继承自
std::exception
,所以都会被这个catch
块捕获 -
捕获后执行异常处理代码,打印错误信息
-
运行示例(内存足够时):
把vector
的大小再加大(内存不足时):
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!