【知识讲解】 究查错误的源头--C++异常机制讲解

目录

前言

Part1. C++异常基础机制

Part1.1. throw、try、catch

Part1.2. 异常对象生命周期

Part1.3. 继承体系与异常捕获

Part1.4. 异常重新抛出

Part2. 异常带来的资源安全问题

Part2.1. 内存泄漏风险

Part2.2. 传统手动解决方案

Part3. 智能指针

Part3.1. auto_ptr

Part3.2. unique_ptr

Part3.3. shared_ptr

Part4. noexcept 异常说明符

Part4.1. 基础语法

Part4.2. 运行时检测函数

Part5. C++标准异常体系

Part10. 结语

前言
正如C语言中有perror等错误处理函数一样,C++也有其独特的错误处理函数,那就是C++的异常处理机制,我们来看看吧。

let's go!!!!!!!!

Part1. C++异常基础机制
头文件:#include <exception>

<1> 传统C语言出错处理:perror、错误码;错误只能在发生点就地处理。

<2> C++异常核心特点:错误不必当场处理,可以跨多层函数向上抛出,由外层合适位置捕获处理。

Part1.1. throw、try、catch
try
{
// 可能产生异常的代码
throw 异常对象;
}
catch(类型1 参数)
{
// 处理对应类型异常
}
catch(类型2 参数)
{

}
用码道免费领 1 个月 Token
cpp
运行

<1> throw:抛出异常,一旦执行throw,后续代码全部停止执行,沿着调用栈匹配catch。

<2> 匹配规则:严格匹配类型,优先匹配距离最近的catch。

<3> 异常被成功捕获处理后,程序不会崩溃,执行完catch代码,继续运行后续普通代码。

<4> 若异常一路向上没有任何catch捕获,程序直接崩溃。
补充:catch(...) 万能捕获,可以捕捉任意类型的异常。

Part1.2. 异常对象生命周期
throw s;
用码道免费领 1 个月 Token
cpp
运行
抛出的异常对象,生命周期持续到对应catch处理完毕,类似值返回机制。

Part1.3. 继承体系与异常捕获
//C++标准设计异常基类 std::exception
class exception
{
protected:
string errmsg;
int id;
public:
virtual const char* what() const noexcept;
};
//所有标准异常类(内存异常、算术异常等)公有继承exception。
//利用多态特性:
try
{
// 抛出派生异常子类对象
}
catch(const exception& e)
{
// 所有派生异常全部可以被捕获
}
用码道免费领 1 个月 Token
cpp
运行

最佳实践:捕获基类引用,通过what()虚函数区分具体错误信息。

Part1.4. 异常重新抛出
场景:内层捕获异常,做一部分处理,继续向上层传递异常,交给外层处理。

try
{
//业务代码
}
catch(...)
{
//局部处理逻辑
throw; //不带参数,重新抛出当前捕获的异常
}
典型应用:循环重试机制
while(true)
{
try
{
func();
break; //正常执行成功,跳出循环
}
catch(特定错误 &e)
{
// 可以重试,继续循环
}
catch(...)
{
throw; //无法处理,向上抛出给外层
}
}
用码道免费领 1 个月 Token
cpp
运行

Part2. 异常带来的资源安全问题
Part2.1. 内存泄漏风险
void test()
{
int* p = new int[10];
// 中间代码如果抛出异常
// 直接跳出函数,下方delete无法执行 → 内存泄漏
delete[] p;
}
用码道免费领 1 个月 Token
cpp
运行
根本矛盾:异常会打乱代码执行流程,正常流程下的释放语句可能被跳过。

Part2.2. 传统手动解决方案
<1> 在多处catch中重复编写释放代码,代码臃肿、极易遗漏。

<2> 解决方案思想:RAII 资源自动管理

<3> 把资源托管给局部对象;局部对象生命周期结束自动调用析构释放资源,无论正常退出、异常抛出、return,析构一定会执行。

Part3. 智能指针
智能指针本质:模板类,内部封装裸指针,依靠析构函数自动释放堆内存。

Part3.1. auto_ptr<T>
• 支持拷贝构造,拷贝之后源指针置空(所有权转移);

• 存在严重缺陷,C++11起废弃,不推荐使用。

Part3.2. unique_ptr
<1> 独占所有权:同一时刻资源只能被一个unique_ptr持有;

<2> 禁止拷贝构造、拷贝赋值;

<3> 支持移动语义 std::move;

unique_ptr<int> p1(new int[10]);
// unique_ptr<int> p2 = p1; // 编译报错,禁止拷贝
unique_ptr<int> p2 = std::move(p1); // 移动所有权,p1变为空

用码道免费领 1 个月 Token
cpp
运行
Part3.3. shared_ptr
<1> 允许多个shared_ptr共同持有同一块堆资源;

<2> 底层维护引用计数:

◦ 拷贝构造/赋值:引用计数 +1

◦ 智能指针析构:引用计数 -1;计数归零,释放堆内存;

shared_ptr<int> sp1(new int[10]);
shared_ptr<int> sp2 = sp1; // 允许拷贝,引用计数+1
//经典坑点:循环引用会造成引用计数永远无法归零,内存泄漏。
用码道免费领 1 个月 Token
cpp
运行
Part4. noexcept 异常说明符
Part4.1. 基础语法
// 承诺函数内部不会抛出任何异常
int func() const noexcept;
• noexcept(true):函数保证不抛异常;

• noexcept(false):函数可能抛出异常(默认状态);
用码道免费领 1 个月 Token
cpp
运行
Part4.2. 运行时检测函数
noexcept(表达式);
编译期判断表达式是否有可能抛出异常,返回布尔值。
编译器可以基于noexcept优化代码,减少异常分支开销。

Part5. C++标准异常体系
标准库内置一套完整异常类,全部派生自 std::exception
常见子类:

• std::bad_alloc:new分配内存失败

• std::out_of_range:容器越界访问

• std::invalid_argument:非法参数
统一通过虚函数 virtual const char* what() const noexcept; 返回错误描述字符串。

Part6. 结语
这篇文章我们认识到了异常相关知识,接下来,小编还会带来C++智能指针的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

上一篇 Atlas 500Web界面NPU告警,JPEGD PROC失败
下一篇 Fstab