蔚淦丞
发布于 2026-09-22 / 0 阅读
0
0

智能指针

一、为什么需要智能指针?

C++ 原生裸指针存在三个致命问题:

  1. 忘记 delete → 内存泄漏

  2. 重复 delete → 程序崩溃(野指针)

  3. 异常跳转导致没执行 delete → 泄漏

智能指针核心思想:用 RAII 机制管理堆内存生命周期

智能指针本质是 栈对象,出作用域自动析构,自动释放堆内存,彻底解决手动内存管理问题。

二、四种智能指针总览(C++11 标准)

  • std::unique_ptr:独占所有权(最常用、零开销)

  • std::shared_ptr:共享所有权(引用计数、有开销)

  • std::weak_ptr:弱引用,不计数,解决循环引用

  • std::auto_ptr:C++98 废弃,有坑(拷贝即转移所有权,极其坑,unique_ptr不允许拷贝,只能移动,相当于告知使用者后果)

三、unique_ptr(独占式智能指针)

1. 核心特性

  • 独占所有权:同一时间只能有一个指针管理对象

  • 禁止拷贝(拷贝构造/拷贝赋值 delete)

  • 允许移动(std::move)

  • 无引用计数、开销为 0,等效裸指针

  • 出作用域自动析构释放内存

2. 基本用法

// 创建独占指针
std::unique_ptr<int> ptr(new int(100));

// C++14 推荐:make_unique(更安全、异常安全)
auto ptr = std::make_unique<int>(100);

// 禁止拷贝
// auto ptr2 = ptr;  // 编译报错

// 允许移动
auto ptr2 = std::move(ptr); 

// 手动释放
ptr2.reset();

3. 适用场景

  • 绝大多数单所有者场景(网络连接、上下文对象、文件句柄)

  • 性能要求高、不想有引用计数开销

  • 你的 mempool-cpp、Reactor 连接对象最适合用 unique_ptr

4. 自定义删除器

unique_ptr 支持自定义析构逻辑,适合管理 fd、socket、文件:

auto fdDeleter = [](int* fd) { close(*fd); delete fd; };
std::unique_ptr<int, decltype(fdDeleter)&gt; fdPtr(new int(123), fdDeleter);

四、shared_ptr(共享式智能指针)

1. 核心特性

  • 共享所有权,多个指针可以管理同一个堆对象

  • 内部维护 引用计数(原子变量,线程安全)

  • 每多一个 shared_ptr,计数+1;每析构一个,计数-1

  • 计数为 0 自动释放堆内存

2. 基本用法

// 推荐 make_shared(内存一次性分配,效率更高)
auto p1 = std::make_shared<int>(666);
auto p2 = p1; // 计数 +1

std::cout << p1.use_count() << std::endl; // 2

p1.reset(); // 计数 -1
p2.reset(); // 计数 0,释放内存

3. 开销问题(面试重点)

  • 需要两块内存:对象内存 + 控制块内存(计数、删除器)

  • 引用计数是 atomic 原子操作,多线程安全,但有性能损耗

  • 高频服务、高性能框架不建议大量用 shared_ptr

4. 经典坑:循环引用

struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };

// 循环引用导致内存永远不释放
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a;

两个对象互相持有对方 shared_ptr,引用计数永远不为 0 → 内存泄漏

解决:其中一端改用 weak_ptr

五、weak_ptr(弱引用指针)

1. 核心特性

  • 不增加引用计数

  • 不拥有对象所有权,仅“观察”对象

  • 可以检测对象是否已经销毁

  • 只能从 shared_ptr 构造

2. 核心用法

auto sp = std::make_shared<int>(100);
std::weak_ptr<int> wp = sp;

if (!wp.expired()) {
    // 提升为 shared_ptr 安全使用
    auto p = wp.lock();
}
sp.reset();

3. 解决循环引用(最终正确写法)

struct A { std::shared_ptr<B> b; };
struct B { std::weak_ptr<A> a; }; // 弱引用,不计数

此时生命周期完全正常释放。

六、三种智能指针选型总结

  1. 默认全部用 unique_ptr:性能最高、无开销、最简单、不会循环引用

  2. 确实需要多线程/多所有者共享对象 才用 shared_ptr

  3. 需要观察、不希望影响生命周期 用 weak_ptr

七、重点分析

1. unique_ptr 和 shared_ptr 区别?

unique_ptr 独占所有权、禁止拷贝、无引用计数、零开销;shared_ptr 共享所有权、带原子引用计数、支持拷贝、有性能开销。优先使用 unique_ptr。

2. make_unique / make_shared 优势?

统一内存分配、异常安全、避免裸指针泄露、代码更简洁。make_shared 一次性分配对象+控制块,内存碎片更少。

3. weak_ptr 作用?

解决 shared_ptr 循环引用问题,不增加引用计数,可以安全判断对象是否存活。

4. shared_ptr 线程安全吗?

引用计数线程安全(原子操作),对象本身读写不安全,多线程修改对象需要自己加锁。


评论