跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 智能指针:从原理到实战

深入探讨了 C++ 智能指针的核心机制与实战应用。通过 RAII 原则解决了手动管理内存导致的泄漏问题,详细对比了 unique_ptr、shared_ptr 和 weak_ptr 的所有权模型及适用场景。重点讲解了引用计数原理、循环引用的成因与解决方案,并强调了 make_unique 和 make_shared 工厂函数的优势。遵循这些最佳实践能显著提升代码的安全性与可维护性。

JavaCoder发布于 2026/3/21更新于 2026/10/1892 浏览
C++ 智能指针:从原理到实战

为什么需要智能指针?

在传统的 C++ 开发中,我们习惯用 new 和 delete 手动管理堆内存。但这很容易出问题。

void riskyFunction() {
    MyClass* ptr = new MyClass();
    // ... 业务逻辑 ...
    if (some_condition) {
        throw std::runtime_error("Oops!");
        // 如果这里抛出异常,下面的 delete ptr 不会被执行!
    }
    delete ptr;
}

如果在 delete 之前因为条件判断、异常或者疏忽提前返回,指针指向的内存就永远无法释放,导致内存泄漏。这就是引入智能指针的根本原因。

核心思想:RAII

资源获取即初始化(Resource Acquisition Is Initialization)。原理其实很简单却极其强大:

  • 构造函数中获取资源(分配内存、打开文件、加锁)。
  • 析构函数中释放资源(释放内存、关闭文件、解锁)。

C++ 保证当一个对象离开作用域时(无论是正常退出还是因异常),它的析构函数一定会被自动调用。智能指针正是将 RAII 思想应用于动态内存管理的完美范例,它将裸指针包装成类对象,并在析构时自动执行 delete。

主要智能指针类型

C++11 引入了三种主要的智能指针,位于 <memory> 头文件中。

1. unique_ptr:独占所有权

这是最基础的智能指针,特性非常明确:

  • 独占所有权:一个资源只能被一个 unique_ptr 拥有。
  • 禁止拷贝:不能拷贝构造,也不能拷贝赋值。
  • 支持移动:可以通过 std::move 转移所有权。
  • 零开销:与裸指针相比几乎没有性能损失。

创建时推荐使用工厂函数 make_unique(C++14 引入):

#include <memory>
#include <iostream>

int main() {
    // 方式 1:直接构造(不推荐)
    std::unique_ptr<int> p1(new int(42));
    
    // 方式 2:工厂函数(推荐)
    auto p2 = std::make_unique<int>(100);
    auto p3 = std::make_unique<std::string>("Hello");
    
    std::cout << *p2 << std::endl;
    return 0;
}

2. shared_ptr:共享所有权

当多个地方都需要访问同一个对象时,shared_ptr 是更好的选择。

  • 共享所有权:多个 shared_ptr 可以共同拥有同一个对象。
  • 引用计数:内部维护一个计数器,记录有多少个指针指向该对象。
  • 自动释放:当最后一个指向对象的 shared_ptr 销毁或重置时,对象才会被删除。

注意事项:

  1. 优先使用 make_shared:它是一次内存分配(对象 + 控制块),效率比直接 new 更高。
  2. 不要混用原始指针:不要用同一个裸指针初始化多个 shared_ptr,这会导致重复释放。
  3. 小心循环引用:两个对象互相持有 shared_ptr 会导致引用计数永远不为 0,此时应使用 weak_ptr。
// 正确做法
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ptr2 = ptr1; // 引用计数增加

// 错误做法
MyClass* raw = new MyClass();
std::shared_ptr<MyClass> p1(raw);
std::shared_ptr<MyClass> p2(raw); // 危险!两个独立计数,都会 delete raw

3. weak_ptr:解决循环引用

weak_ptr 同样也是 C++11 的智能指针,它的出现主要是为了解决 shared_ptr 的循环引用问题。

  • 它不拥有对象,不会增加引用计数。
  • 它可以像指针一样使用,但需要先通过 lock() 转换为 shared_ptr 才能安全访问。
  • 常用于打破循环引用或作为临时观察者。
class Node {
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // 使用 weak_ptr 避免循环引用
};

工厂函数:make_unique / make_shared

  • make_unique<T>(args...):创建 unique_ptr。
  • make_shared<T>(args...):创建 shared_ptr。

它们不仅更安全(防止异常导致的内存泄漏),对于 shared_ptr 还能优化内存分配(一次分配对象和控制块)。

总结

特性unique_ptrshared_ptrweak_ptr
所有权独占共享不拥有
拷贝禁止允许允许
移动允许允许允许
性能零开销有开销有开销
适用场景单一明确所有者多个不确定的所有者打破循环引用、缓存

最佳实践建议:

  1. 首选 unique_ptr:除非你需要共享所有权,否则默认使用它,最安全高效。
  2. 使用工厂函数:始终优先使用 make_unique 和 make_shared。
  3. 避免裸指针:将所有的 new 和 delete 替换为智能指针。
  4. 警惕循环引用:涉及双向关系时,记得检查是否需要 weak_ptr。

目录

  1. 为什么需要智能指针?
  2. 核心思想:RAII
  3. 主要智能指针类型
  4. 1. unique_ptr:独占所有权
  5. 2. shared_ptr:共享所有权
  6. 3. weak_ptr:解决循环引用
  7. 工厂函数:makeunique / makeshared
  8. 总结

更多推荐文章

查看全部
  • Java 面向对象核心:深入理解 this 关键字
  • 2025 年 12 款 AI 写小说工具实测及优劣对比
  • 2026 年高薪就业赛道:AI 大数据、大模型、AIGC 与云计算
  • 前端首屏加载优化落地清单与实操指南
  • Cursor 与 GitHub Copilot 深度对比:架构、性能与选型指南
  • 蒙特卡罗树搜索 (MCTS) 原理与代码实例
  • Java 运算符详解:算术、逻辑与位运算
  • 2025年主流AI代码生成工具深度测评与选型指南
  • KES 数据库运维核心:资源回收与膨胀防治
  • C++ Elasticsearch 第三方库 elasticlient 详解
  • HivisionIDPhotos 开源 AI 证件照工具功能解析与部署指南
  • 网络编程核心:路由表、MTU/MSS、ARP、NAT 及代理技术解析
  • Mac 环境下 PHP 调用 Java JAR 包指南
  • Spring 国际化核心原理详解:4 大组件实现企业级多语言开发
  • Java Map 常用方法与核心实现类深度解析
  • 小米智能家居 Miloco 分离式部署指南
  • AWS Kiro 账号池管理系统:Amazon Q Developer 转 OpenAI 兼容代理
  • MIT 电机模式控制详解:参数配置与调试实战
  • 使用 OpenClaw 的 Skills 对接本地系统
  • MHDDoS Python 网络压力测试工具架构解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online