C++ 智能指针的底层实现不仅依赖 RAII 核心思想(资源获取即初始化),还深度融合了多种经典设计模式,这些模式共同支撑起“自动资源管理、灵活所有权控制、可扩展接口”的核心能力。
以下是智能指针底层关键设计模式的详细解析,结合底层实现逻辑说明模式的应用场景和核心作用:
一、包装器模式(Wrapper Pattern)/ 装饰器模式(Decorator Pattern)
模式定义
- 核心目标:为一个对象(或资源)提供“包装层”,封装内部实现细节,同时保留原对象的核心接口(或模拟相似接口),并在包装层中添加额外功能(如资源管理、访问控制)。
- 本质:“包装原有对象,增强功能且不改变接口”。
智能指针中的应用(所有智能指针的基础)
智能指针的本质是 “裸指针的包装器”,底层通过封装 T* 裸指针,重载 operator*、operator-> 等运算符模拟指针行为,同时在包装层中添加“自动释放资源”的核心功能。
底层实现细节
以 unique_ptr 为例,其底层结构直接体现包装器模式:
template <typename T>
class unique_ptr {
private:
T* ptr_; // 被包装的裸指针(核心资源)
public:
// 模拟裸指针接口
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
// 包装层添加的额外功能(资源管理)
~unique_ptr() { delete ptr_; } // 自动释放
};
- 原对象:裸指针
T*(提供直接访问内存的核心能力)。 - 包装层:
unique_ptr类(封装ptr_,隐藏裸指针的直接操作,添加析构时自动释放的功能)。 - 接口一致性:通过重载
*和->,让unique_ptr用法与裸指针完全一致(p->func()、*p),降低使用成本。
核心价值
- 隐藏裸指针的手动管理(
new/delete),避免内存泄漏。 - 保留指针的直观用法,无需修改业务代码逻辑。
二、引用计数模式(Reference Counting)
模式定义
- 核心目标:通过一个“共享计数器”跟踪引用同一资源的对象数量,当计数器为 0 时,自动释放资源(无对象引用该资源)。
- 本质:“共享资源的生命周期管理,通过计数避免重复释放和内存泄漏”。
智能指针中的应用(shared_ptr + weak_ptr 的核心)
shared_ptr 是引用计数模式的经典实现,底层通过 控制块(Control Block) 存储共享计数器,实现多对象共享资源所有权。
底层实现细节
- 计数器存储:控制块中包含两个原子计数器(
shared_count强引用计数、weak_count弱引用计数),所有共享同一资源的shared_ptr指向同一个控制块。 - 计数操作逻辑:
- 新建
shared_ptr(如make_shared):计数器初始化为 1。 - 拷贝
shared_ptr:shared_count原子递增(线程安全)。 - 析构
shared_ptr:shared_count原子递减,若为 0 则释放资源。 weak_ptr操作:仅修改weak_count,不影响资源生命周期。
- 新建
模式作用
- 解决“多对象共享同一资源”的生命周期管理问题,避免重复释放(计数器确保仅当最后一个引用者析构时释放)。
- 支持灵活的所有权共享(如函数间传递、容器存储),无需手动跟踪引用关系。
关键区别:引用计数 vs 垃圾回收
- 引用计数是 “即时性、手动可控” 的(C++ 中由智能指针主动管理,无运行时垃圾回收器)。
- 计数器存储在控制块中,与资源绑定,无全局扫描开销(但需原子操作保证线程安全)。
三、策略模式(Strategy Pattern)
模式定义
- 核心目标:定义一系列“算法(或行为)”,将每个算法封装为独立的“策略类”,并使策略可动态替换(或编译时指定),客户端可根据需求选择不同策略,无需修改核心逻辑。
- 本质:“算法的封装与解耦,支持灵活替换”。
智能指针中的应用(删除器的设计)
智能指针的 删除器(Deleter) 是策略模式的典型应用:删除器本质是“资源释放策略”,默认提供 std::default_delete(delete/delete[]),同时支持自定义释放策略(如释放文件句柄、数组、第三方库资源)。
底层实现细节
根据智能指针的类型,删除器的“策略封装”有两种方式:
-
unique_ptr:编译时策略绑定(模板参数)- 删除器类型是模板参数(
typename Deleter = std::default_delete<T>),编译时确定策略,无类型擦除开销。 - 不同删除器对应不同的
unique_ptr类型(如unique_ptr<T, FileDeleter>),策略与容器强绑定。
// 策略1:默认删除器(delete) std::unique_ptr<int> p1(new int(10)); // 策略2:自定义文件删除器(fclose) struct FileDeleter { void operator()(FILE* fp) { fclose(fp); } }; std::unique_ptr<FILE, FileDeleter> p2(fopen("test.txt", "r")); - 删除器类型是模板参数(
-
shared_ptr:运行时策略绑定(类型擦除)- 删除器存储为
std::function<void(void*)>,通过类型擦除隐藏策略类型,不同删除器可对应同一shared_ptr类型(如shared_ptr<FILE>)。 - 策略可动态替换(构造时传入不同删除器),灵活性更高,但有轻微类型擦除开销。
// 策略1:默认删除器 std::shared_ptr<int> p1(new int(10)); // 策略2:lambda 自定义删除器(释放数组) std::shared_ptr<int> p2(new int[5], [](int* p) { delete[] p; }); - 删除器存储为
模式作用
- 解耦“智能指针核心逻辑”与“资源释放策略”:智能指针无需关心资源是如何释放的,只需调用删除器接口。
- 支持多样化资源管理:除了动态内存,还可管理文件句柄、socket、数据库连接等,只需提供对应的删除策略。
四、观察者模式(Observer Pattern)
模式定义
- 核心目标:定义“被观察者”与“观察者”的一对多依赖关系,当被观察者的状态发生变化时(如资源释放),所有观察者会被间接通知(或可查询状态变化),从而做出响应。
- 本质:“状态同步与解耦,观察者不影响被观察者的核心逻辑”。
智能指针中的应用(weak_ptr + shared_ptr 的关系)
weak_ptr 是“观察者”,shared_ptr 管理的资源是“被观察者”,底层通过控制块的引用计数实现状态观察,不持有资源所有权。
底层实现细节
- 被观察者(资源):状态由
shared_count决定(shared_count > 0表示资源存活,=0表示资源释放)。 - 观察者(
weak_ptr):- 仅持有控制块指针(无数据指针),不修改
shared_count(不影响资源生命周期)。 - 通过
expired()接口查询被观察者状态(本质是读取shared_count)。 - 通过
lock()接口尝试“获取被观察者的访问权”:若资源存活,则返回有效shared_ptr(shared_count递增);若资源已释放,则返回空shared_ptr。
- 仅持有控制块指针(无数据指针),不修改
模式作用
- 解决
shared_ptr的 循环引用 问题:weak_ptr作为观察者,不增加强引用计数,打破“强引用闭环”。 - 安全观察共享资源:避免直接访问已释放的资源(通过
lock()确保访问时资源存活)。
关键区别:主动通知 vs 被动查询
传统观察者模式是“被观察者主动通知观察者”,而 weak_ptr 采用“被动查询”模式(通过 expired()/lock() 主动查询状态),原因是:
- 避免额外的通知机制开销(如回调函数、链表存储观察者)。
- 符合 C++“零额外开销”设计理念,仅在需要时查询状态。
五、代理模式(Proxy Pattern)
模式定义
- 核心目标:为其他对象提供“代理”,控制对原对象的访问,代理可在访问前后添加额外逻辑(如生命周期管理、权限校验)。
- 本质:“间接访问对象,代理承担额外职责”。
智能指针中的应用(整体行为定位)
智能指针本质是 裸指针的代理,通过代理控制对裸指针的访问,同时承担“资源自动释放”的额外职责。
底层实现细节
- 原对象:裸指针
T*(直接指向内存资源)。 - 代理对象:
unique_ptr/shared_ptr等智能指针。 - 代理的核心控制逻辑:
- 访问控制:通过
operator*/operator->间接访问原对象,隐藏裸指针的直接操作(避免误修改指针地址)。 - 生命周期管理:代理对象析构时自动释放原对象(无需用户手动
delete)。 - 安全校验:如
weak_ptr::lock()会校验资源是否存活,避免野指针访问。
- 访问控制:通过
与包装器模式的区别
- 包装器模式:更侧重“保留原接口、封装内部实现”(如智能指针模拟指针接口)。
- 代理模式:更侧重“控制访问、添加额外职责”(如智能指针控制资源释放、安全校验)。
- 智能指针同时具备两种模式的特性:既是裸指针的包装器(保留接口),也是裸指针的代理(控制访问和生命周期)。
六、关键补充:RAII 是设计思想,不是设计模式
很多人会将 RAII 归为设计模式,但严格来说:
- RAII 是“资源管理的核心思想”(资源获取即初始化,资源与对象生命周期绑定),而上述模式是“实现 RAII 思想的具体手段”。
- 例如:
unique_ptr通过“包装器模式 + RAII”实现独占资源的自动释放;shared_ptr通过“引用计数模式 + RAII”实现共享资源的自动释放。
总结:各模式在智能指针中的核心作用
| 设计模式 | 应用对象 | 核心作用 | 底层体现 |
|---|---|---|---|
| 包装器模式 | 所有智能指针 | 封装裸指针,模拟指针接口 | 重载 */->,隐藏 ptr_ 成员 |
| 引用计数模式 | shared_ptr/weak_ptr | 共享资源生命周期管理,避免内存泄漏 | 控制块中的 shared_count/weak_count |
| 策略模式 | 所有智能指针(删除器) | 解耦释放逻辑,支持多样化资源管理 | 模板参数(unique_ptr)或 std::function(shared_ptr) |
| 观察者模式 | weak_ptr/shared_ptr | 观察共享资源状态,打破循环引用 | expired()/lock() 接口查询 shared_count |
| 代理模式 | 所有智能指针 | 控制裸指针访问,承担自动释放职责 | 间接访问资源,析构时释放原对象 |
2020

被折叠的 条评论
为什么被折叠?



