首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++面试周刊(7):unique_ptr 与 shared_ptr 的实现原理

C++面试周刊(7):unique_ptr 与 shared_ptr 的实现原理

作者头像
早起的鸟儿有虫吃
发布2025-10-10 12:06:37
发布2025-10-10 12:06:37
900
代码可运行
举报
运行总次数:0
代码可运行

在 C++ 中,手动管理资源(new/delete)容易出错,导致:

内存泄漏

重复释放

异常安全问题

为了解决这些问题,C++11 引入了 智能指针

std::unique_ptr:独占所有权

std::shared_ptr:共享所有权,通过引用计数管理生命周期

我们从原理、性能和应用场景三个维度来解析。


一、技术背景与设计初衷

1

unique_ptr

目标:独占资源,不可复制,只能移动(move)

使用场景:资源唯一所有权,RAII 风格自动释放

核心问题:

避免内存泄漏

支持移动语义

1

shared_ptr

目标:多方共享资源,引用计数控制生命周期

使用场景:多线程共享对象、缓存管理

核心问题:

自动管理生命周期

处理多线程环境下的引用计数(atomic)

避免循环引用问题


二、unique_ptr 实现原理

1. 内部结构

代码语言:javascript
代码运行次数:0
运行
复制
template<typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
    T* ptr; // 原始指针
    Deleter deleter; // 删除器
public:
    explicit unique_ptr(T* p = nullptr) noexcept : ptr(p) {}
    ~unique_ptr() { if(ptr) deleter(ptr); }

    // 不可拷贝
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

    // 可移动
    unique_ptr(unique_ptr&& u) noexcept : ptr(u.ptr) { u.ptr = nullptr; }
    unique_ptr& operator=(unique_ptr&& u) noexcept {
        if(this != &u) {
            reset(u.ptr);
            u.ptr = nullptr;
        }
        return *this;
    }

    T* get() const noexcept { return ptr; }
    T& operator*() const noexcept { return *ptr; }
    T* operator->() const noexcept { return ptr; }

    void reset(T* p = nullptr) {
        if(ptr) deleter(ptr);
        ptr = p;
    }
};

2. 核心原理

独占所有权:唯一指针拥有资源,拷贝被禁用

移动语义:通过移动实现资源转移

RAII:生命周期结束自动释放

3. 性能特点

内存占用小(只存储原始指针 + 删除器)

无额外引用计数开销

编译期开销低

异常安全,避免泄漏


三、shared_ptr 实现原理

1. 内部结构

shared_ptr 核心在于 引用计数控制块(control block)

代码语言:javascript
代码运行次数:0
运行
复制
shared_ptr<T>
├── T* ptr           // 实际对象指针
└── ControlBlock* cb // 引用计数和删除器

控制块结构示例

代码语言:javascript
代码运行次数:0
运行
复制
struct ControlBlock {
    std::atomic<size_t> use_count;   // 强引用计数
    std::atomic<size_t> weak_count;  // 弱引用计数
    Deleter deleter;                 // 删除器
    T* ptr;                          // 指向对象
};

2. 核心逻辑

构造 shared_ptr:初始化 use_count = 1

拷贝 shared_ptruse_count++(原子操作)

析构 shared_ptruse_count--,若为 0 调用删除器销毁对象

weak_ptr:不增加 use_count,仅增加 weak_count,用于解决循环引用

3. 关键实现细节

线程安全std::atomic 保证引用计数在多线程下正确

控制块分离

对象指针和控制块分离,可以支持 make_shared 内联分配(减少内存碎片)

循环引用问题

两个 shared_ptr 互相引用会造成引用计数不为 0

需要 weak_ptr 解决


四、性能分析

特性

unique_ptr

shared_ptr

内存占用

小(指针+删除器)

控制块额外开销,atomic计数

拷贝/移动开销

禁止拷贝,移动开销小

拷贝需要原子操作,移动开销小

多线程安全

依赖外部保护

引用计数原子操作保证安全

循环引用风险

有,需要 weak_ptr

适用场景

独占资源,RAII

多方共享对象管理,缓存,异步任务


五、在分布式系统中的应用

1

unique_ptr

Ceph 的对象缓存(ObjectCacher)中,每个缓存条目独占内存块

TiDB Executor 局部临时对象管理

1

shared_ptr

Ceph 多线程 I/O 回调对象共享

TiDB RPC/异步任务的共享上下文(Context)

核心思想:能用 unique_ptr 就用 unique_ptr,能静态分析生命周期,避免 atomic 开销;需要多方共享才用 shared_ptr,并结合 weak_ptr 避免循环引用。


六、总结

unique_ptr

独占资源,低开销,RAII 自动释放

通过 移动语义实现所有权转移

shared_ptr

共享资源,通过 引用计数 + 控制块管理生命周期

支持多线程,但有性能开销

循环引用需要 weak_ptr 解决

在分布式系统

hotspot 对象尽量用 unique_ptr

异步回调、共享上下文用 shared_ptr

一句话总结 “能唯一所有权就用 unique_ptr,必须共享就用 shared_ptr,控制引用计数就是控制你的性能。”


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、技术背景与设计初衷
  • 二、unique_ptr 实现原理
    • 1. 内部结构
    • 2. 核心原理
    • 3. 性能特点
  • 三、shared_ptr 实现原理
    • 1. 内部结构
    • 2. 核心逻辑
    • 3. 关键实现细节
  • 四、性能分析
  • 五、在分布式系统中的应用
  • 六、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档