C++异常与RAII智能指针笔记

一、异常的抛出和捕获

程序出现问题时,可以通过throw抛出对象引发异常;抛出对象的类型,以及当前调用链,决定由哪个catch块处理。

  • 匹配规则:选中的catch是调用链里类型匹配、且离抛出位置最近的那个。

  • throw执行后,throw后面代码不再执行;程序跳转到匹配的catchcatch可以在当前函数,也可以在上层调用函数。控制权转移到catch,会沿着调用链提前退出函数。

  • 一旦进入异常处理流程,栈上创建的对象都会销毁。

  • 抛出异常对象时,会生成异常对象拷贝;catch处理完后销毁拷贝对象(C++11 后优化,减少拷贝)。

二、栈展开 (stack unwinding)

抛出异常后,暂停当前函数执行,向上寻找匹配catch

  1. 先检查throw是否在try块内,有匹配catch就跳到catch执行。

  2. 当前函数没有try/catch,或者catch类型不匹配:退出当前函数,继续在外层调用函数查找匹配catch,这个过程就是栈展开

  3. 一直找到main函数仍无匹配catch:调用库函数terminate终止程序。

  4. 找到匹配catch并处理完毕,catch之后代码继续执行。

三、查找匹配的处理代码

  1. 默认:抛出对象类型和catch类型完全匹配;多个匹配时,选择位置更近的。

  2. 允许少量隐式转换:非常量→常量;数组→数组首元素指针;函数→函数指针;派生类→基类(继承体系中非常常用)。

  3. 若到main还没有匹配异常处理器,程序终止。一般会在maincatch(...)捕获任意类型异常,但无法得知具体错误信息。

四、异常规范(异常说明)

用来标注函数会不会抛出异常,方便调用者理解。

  • C++98:函数参数列表后throw()表示不抛异常;throw(类型1,类型2...)列出可能抛出的异常类型。写法繁琐,实践很少使用。

  • C++11:简化,noexcept修饰函数代表不会抛出异常;不写则代表可能抛出异常。

    编译器不会在编译期校验 noexcept :就算函数内部有throw或者调用可能抛异常的函数,编译依旧可以通过(部分编译器告警)。 如果标记noexcept的函数抛出异常,程序直接调用terminate终止。

  • noexcept(表达式)还可以作为运算符:检测表达式是否可能抛异常,返回false可能抛,返回true不会抛。 示例:

复制代码
vector<int> v;
v.size();   // noexcept,不会抛异常
v.push_back(1); // 可能抛异常(内存分配失败)

五、RAII 和智能指针设计思路

RAII(Resource Acquisition Is Initialization) :资源获取即初始化。 核心思想:利用对象生命周期管理资源(堆内存、文件句柄、网络连接、互斥锁等)。

  • 获取资源时,交给一个对象;资源的访问交给对象控制。

  • 对象生命周期内资源保持有效。

  • 对象析构的时候自动释放资源。

好处:正常 return,或者异常导致栈展开,都会调用析构函数,保证资源释放,杜绝内存泄漏

简易智能指针示例:

复制代码
template<class T>
class SmartPtr
{
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {}
    ~SmartPtr()
    {
        cout << "delete:" << _ptr << endl;
        delete[] _ptr;
    }
public:
    T* _ptr;
};

void Func()
{
    SmartPtr<int> sp1 = new int[10];
    SmartPtr<int> sp2 = new int[10];
    int len, time;
    cin >> len >> time;
    cout << Divide(len, time) << endl;
}

哪怕Divide抛出异常,栈展开时sp1 sp2析构,自动释放 new 出来的数组。

六、C++ 标准库智能指针(<memory>头文件)

weak_ptr外,其余都遵循 RAII 思想,差异主要在拷贝管理。

  1. auto_ptr(C++98,已废弃) 拷贝时转移管理权,被拷贝对象悬空,访问会报错;C++11 之后不推荐,项目禁用。

  2. unique_ptr(C++11) 唯一指针,不支持拷贝,只支持移动。不需要共享资源的场景首选。

  3. shared_ptr(C++11) 共享指针,支持拷贝与移动;底层依靠引用计数实现。

    1. 多个shared_ptr指向同一块资源,引用计数 + 1;

    2. 销毁一个shared_ptr,引用计数 - 1;

    3. 引用计数减到 0 时,释放管理的堆资源。

  4. weak_ptr(C++11) 弱指针,不参与 RAII,不增加引用计数;专门用来解决shared_ptr循环引用造成的内存泄漏。

智能指针补充要点

  • 默认析构使用delete释放资源;如果不是new出来的资源交给智能指针,析构会崩溃。可以自定义删除器,修改释放逻辑。

  • unique_ptrshared_ptr支持数组版本:unique_ptr<Date[]> up1(new Date[5]);

  • make_shared<T>(args):直接构造资源对象,返回shared_ptr<T>,推荐优先使用。

  • shared_ptr/unique_ptr支持operator bool:智能指针对象可以直接if(sp)判断是否管理有效资源。

  • 构造函数使用explicit修饰:禁止普通裸指针隐式转换成智能指针。

相关推荐
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<四十二>:手势识别
c++·人工智能·opencv·计算机视觉·手势识别
BD_Marathon1 小时前
调用DeepSeek官网的DeepSeek模型
开发语言·chrome·python
草莓熊Lotso1 小时前
【Redis 初阶】C++ 客户端实战:从 RESP 协议到 redis-plus-plus 工程化用法
linux·开发语言·网络·数据库·c++·redis·缓存
郝学胜-神的一滴1 小时前
C++11 工程级应用 10:减少拷贝,让容器跑得更快
服务器·开发语言·c++·游戏引擎·opengl
玖玥拾1 小时前
Lua 基础语法(八) Unity Huatuo (HybridCLR)
开发语言·unity·游戏引擎·lua
Wang's Blog2 小时前
Java框架快速入门: Spring Security+OAuth2之环境配置与多环境部署
java·开发语言·spring
小灰灰搞电子2 小时前
Rust suppaftp 库详解:基于 FTP 客户端实战指南
开发语言·后端·rust
编码浪子2 小时前
Rust unsafe 与 FFI 互操作生产级实战:把危险关进笼子的四道闸门
开发语言·后端·rust
YHHLAI2 小时前
基于 Next.js 16 + Redis 的全栈笔记博客系统
javascript·redis·笔记