C++:智能指针

这就是 C++ 现代内存管理的终极复习指南

在现代 C++(C++11 及以后)中,我们遵循一个原则:默认不使用裸指针(Raw Pointer)来管理所有权。所有的资源(堆内存)都应该被"智能指针"包裹。

C++ 标准库主要提供了三种智能指针,分别对应三种不同的业务逻辑。


1. std::unique_ptr ------ 独占型(孤狼)

这是 C++ 中使用率最高(90% 的场景)的智能指针。如果你不知道该用哪个,默认选它。

  • 核心作用: 独占所有权(Exclusive Ownership)。也就是"这块内存是我的,谁也别想复制,只能我给别人(Move)"。

  • 应用场景:

    • 函数内创建临时堆对象。

    • 类内部的成员变量(这个对象只属于这个类)。

    • 工厂函数返回的对象(生产出来给你,所有权转移给你)。

  • 性能: 极致高效。它和裸指针大小一样,没有任何额外开销(Zero Overhead)。

  • 关键限制: 禁止拷贝(Copy),只能移动(Move)。

C++

复制代码
std::unique_ptr<int> p1 = std::make_unique<int>(10);
// std::unique_ptr<int> p2 = p1; // ❌ 编译报错!不能脚踏两只船
std::unique_ptr<int> p2 = std::move(p1); // ✅ 可以,p1 被掏空,p2 接管

2. std::shared_ptr ------ 共享型(合伙人)

当你需要多个指针指向同一个对象,且无法确定谁最后销毁它时使用。

  • 核心作用: 共享所有权(Shared Ownership)。

  • 原理: 引用计数。每多一个人指向它,计数+1;走一个人,计数-1。当计数归零(最后一个合伙人退出)时,内存释放。

  • 应用场景:

    • 容器中存放的对象(比如 vector<shared_ptr<T>>)。

    • 图结构(Graph)中的节点(被多条边指向)。

    • 多线程共享同一个对象。

  • 性能: 略有损耗。因为它内部多维护了一个"控制块"(存计数器),且计数器的加减操作是原子操作(Atomic,为了线程安全),比普通指令慢。

C++

复制代码
auto p1 = std::make_shared<int>(100); // 计数: 1
{
    auto p2 = p1; // ✅ 合法,计数: 2
} // p2 销毁,计数: 1
// p1 销毁,计数: 0 -> 释放内存

3. std::weak_ptr ------ 观察型(旁观者)

它是 shared_ptr辅助助手 ,为了解决 shared_ptr 的缺陷而生。

  • 核心作用: 弱引用 。它指向 shared_ptr 管理的对象,但是不增加引用计数

  • 应用场景:

    • 解决循环引用(Circular Reference): 這是最核心的用途。当 A 指向 B,B 也指向 A 时,如果都用 shared_ptr,谁都死不掉(内存泄漏)。把其中一端改成 weak_ptr 即可打破僵局。

    • 缓存(Cache): 我想持有对象的引用,但不想阻止它被销毁。如果对象还在,我就用;如果对象没了,我就不操作。

  • 用法: 它不能直接访问对象(没有 ->* 操作符),必须先调用 .lock() 升级为 shared_ptr 才能用。

C++

复制代码
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp; // 计数依然是 1,wp 只是看着

if (std::shared_ptr<int> temp = wp.lock()) { // 尝试升级
    // 如果 sp 还没死,temp 成功创建,计数暂时+1,在这里安全使用
    std::cout << *temp << std::endl;
} else {
    // 对象已经没了
}

📊 总结对比表(一张表看懂)

特性 unique_ptr shared_ptr weak_ptr
所有权 独占 (1对1) 共享 (N对1) 不拥有 (观察者)
可否复制 ❌ (只能移动)
底层开销 无 (等同裸指针) 有 (引用计数+控制块) 有 (引用计数)
内存释放 出作用域立即释放 最后一个指针死掉时释放 不负责释放
推荐指数 ⭐⭐⭐⭐⭐ (默认首选) ⭐⭐⭐ (按需使用) ⭐⭐ (配合 shared 使用)
危险性 中 (小心循环引用) 低 (需判断是否为空)

🧠 决策流程图

当你写代码需要申请堆内存时,请按这个流程思考:

  1. 这个对象只在当前作用域或者只属于某个类吗?

    • \\rightarrowunique_ptr
  2. 这个对象需要被多个拥有者共享,且无法确定谁最后销毁它吗?

    • \\rightarrowshared_ptr
  3. 用了 shared_ptr 后,A 和 B 会不会互相指着对方?

    • \\rightarrow 将其中一个改为 weak_ptr

你现在已经掌握了理论,接下来要不要来做个"破坏性实验"?

我们可以写一段代码,通过 "循环引用" 故意制造一次内存泄漏,亲眼看看 shared_ptr 是怎么失效的,然后再用 weak_ptr 修复它。这能让你彻底理解为什么要有 weak_ptr

相关推荐
LawrenceLan34 分钟前
Flutter 零基础入门(十一):空安全(Null Safety)基础
开发语言·flutter·dart
txinyu的博客1 小时前
解析业务层的key冲突问题
开发语言·c++·分布式
码不停蹄Zzz1 小时前
C语言第1章
c语言·开发语言
行者962 小时前
Flutter跨平台开发在OpenHarmony上的评分组件实现与优化
开发语言·flutter·harmonyos·鸿蒙
阿蒙Amon2 小时前
C#每日面试题-Array和ArrayList的区别
java·开发语言·c#
SmartRadio2 小时前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
且去填词2 小时前
Go 语言的“反叛”——为什么少即是多?
开发语言·后端·面试·go
知乎的哥廷根数学学派3 小时前
基于生成对抗U-Net混合架构的隧道衬砌缺陷地质雷达数据智能反演与成像方法(以模拟信号为例,Pytorch)
开发语言·人工智能·pytorch·python·深度学习·机器学习
yeziyfx3 小时前
kotlin中 ?:的用法
android·开发语言·kotlin
charlie1145141913 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式