当前位置: 首页 > news >正文

全面理解-c++11中的智能指针

在 C++ 中,智能指针(Smart Pointers) 是用于自动管理动态分配内存的类模板,遵循 RAII(Resource Acquisition Is Initialization) 原则,确保资源在生命周期结束时被正确释放,避免内存泄漏。C++11 引入了三种主要的智能指针:std::unique_ptrstd::shared_ptr 和 std::weak_ptr,取代了 C++98 中不安全的 std::auto_ptr


1. 为什么需要智能指针?

  • 手动管理内存的痛点

    • 忘记 delete 导致内存泄漏。

    • 重复 delete 导致未定义行为。

    • 异常安全问题(未捕获异常时资源无法释放)。

  • 智能指针的核心作用

    • 自动释放内存:对象生命周期结束时自动调用 delete

    • 明确所有权语义:通过所有权模型管理资源。


2. 主要智能指针类型

(1) std::unique_ptr(独占所有权)
  • 所有权模型:唯一拥有资源,不可复制,但可通过 std::move 转移所有权。

  • 适用场景

    • 资源有唯一拥有者。

    • 需要轻量级、零开销的内存管理。

  • 基本用法

    #include <memory>// 创建 unique_ptr
    std::unique_ptr<int> ptr1 = std::make_unique<int>(10);  // C++14 起推荐
    std::unique_ptr<int> ptr2(new int(20));                 // 直接构造// 转移所有权
    std::unique_ptr<int> ptr3 = std::move(ptr1);  // ptr1 变为 nullptr// 自定义删除器(可选)
    auto deleter = [](int* p) { delete p; };
    std::unique_ptr<int, decltype(deleter)> ptr4(new int(30), deleter);
(2) std::shared_ptr(共享所有权)
  • 所有权模型:通过引用计数(use_count())管理资源,多个指针共享所有权。

  • 适用场景

    • 多个对象需要共享同一资源。

    • 资源生命周期不确定,需自动管理。

  • 基本用法

    // 创建 shared_ptr
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);  // 推荐(高效)
    std::shared_ptr<int> ptr2(new int(20));                 // 直接构造// 共享所有权
    std::shared_ptr<int> ptr3 = ptr1;  // 引用计数 +1(ptr1.use_count() == 2)// 自定义删除器(可选)
    std::shared_ptr<int> ptr4(new int(30), [](int* p) { delete p; });
(3) std::weak_ptr(弱引用)
  • 所有权模型:不增加引用计数,用于解决 shared_ptr 的循环引用问题。

  • 适用场景

    • 观察 shared_ptr 管理的资源,不参与所有权管理。

    • 打破 shared_ptr 的循环引用(如双向链表、观察者模式)。

  • 基本用法

    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    std::weak_ptr<int> weakPtr = sharedPtr;// 使用时提升为 shared_ptr
    if (auto tempPtr = weakPtr.lock()) {  // 检查资源是否有效std::cout << *tempPtr << std::endl;  // 输出 42
    }
(4) std::auto_ptr(已弃用)
  • 问题:所有权转移语义不明确(通过拷贝构造函数转移所有权),易导致悬空指针。

  • 替代方案:使用 std::unique_ptr


3. 智能指针的核心对比

特性std::unique_ptrstd::shared_ptrstd::weak_ptr
所有权独占共享无(弱引用)
拷贝语义禁止(只能移动)允许(引用计数增加)允许(不增加引用计数)
性能开销引用计数操作(原子操作)
循环引用处理不适用无法解决可解决
自定义删除器支持(模板参数)支持(构造函数参数)不适用

4. 使用建议

  1. 优先使用 std::make_unique 和 std::make_shared

    • 更高效(减少内存分配次数)。

    • 异常安全。

    auto ptr = std::make_shared<int>(42);  // 替代 new
  2. 避免裸指针与智能指针混用

    int* rawPtr = new int(10);
    std::shared_ptr<int> ptr(rawPtr);  // ❌ 危险:多个 shared_ptr 可能管理同一裸指针
  3. 解决循环引用

    • 使用 std::weak_ptr 断开 shared_ptr 的循环依赖。

    class B;  // 前向声明class A {
    public:std::shared_ptr<B> bPtr;
    };class B {
    public:std::weak_ptr<A> aPtr;  // 使用 weak_ptr 代替 shared_ptr
    };
  4. 传递智能指针的规则

    • 函数参数

      • 如果函数需要接管所有权 → 按值传递 std::unique_ptr

      • 如果函数只是使用资源 → 传递裸指针或引用。

      void takeOwnership(std::unique_ptr<int> ptr);  // 接管所有权
      void useResource(const int* ptr);              // 仅使用资源


5. 智能指针的底层原理

  • std::unique_ptr

    • 内部封装一个裸指针,删除时调用 delete 或自定义删除器。

    • 禁止拷贝构造函数和拷贝赋值运算符。

  • std::shared_ptr

    • 包含两个指针:一个指向对象,一个指向控制块(含引用计数和删除器)。

    • 引用计数为 0 时释放资源。

  • std::weak_ptr

    • 不增加引用计数,但能检测资源是否有效。


6. 示例代码

(1) unique_ptr 管理动态数组
// 管理动态数组(C++11 需要指定删除器,C++14 起可直接用 unique_ptr<T[]>)
std::unique_ptr<int[]> arr(new int[5]{1, 2, 3, 4, 5});
arr[0] = 10;
(2) shared_ptr 的循环引用问题
#include <memory>class Node {
public:std::shared_ptr<Node> next;
};int main() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;  // node1 引用 node2node2->next = node1;  // node2 引用 node1 → 循环引用,内存泄漏!return 0;
}

解决方案:将其中一个 shared_ptr 替换为 weak_ptr


总结

智能指针是现代 C++ 内存管理的核心工具,通过明确所有权和自动资源释放,显著提升代码安全性和可维护性。根据场景选择:

  • 唯一所有权 → std::unique_ptr

  • 共享所有权 → std::shared_ptr

  • 弱引用观察 → std::weak_ptr

遵循 RAII 原则,避免手动 new/delete,是编写高质量 C++ 代码的关键。

http://www.lryc.cn/news/534492.html

相关文章:

  • 【jmeter】在windows中,创建的变量,在jmeter中,读取变量失败的问题,路径问题
  • 【CubeMX-HAL库】STM32F407—无刷电机学习笔记
  • 使用 POI-TL 和 JFreeChart 动态生成 Word 报告
  • xxl-job的分片广播
  • MobaXterm破解会话上限限制
  • vscode设置保存时自动缩进和格式化
  • 一键查看电脑各硬件详细信息 轻松查看电脑硬件参数
  • 【C++11】lambda和包装器
  • react redux用法学习
  • 前端HTML标签 meta中常见的一些属性
  • 127,【3】 buuctf [NPUCTF2020]ReadlezPHP
  • 继承(python)
  • 驱动开发系列36 - Linux Graphics 2D 绘制流程
  • STL函数算法笔记
  • 【Vue】在Vue3中使用Echarts的示例 两种方法
  • 小红书自动化:如何利用Make批量生成爆款笔记
  • 学习率调整策略 | PyTorch 深度学习实战
  • DeepSeekMoE 论文解读:混合专家架构的效能革新者
  • 以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案:
  • 【JVM详解五】JVM性能调优
  • 2.10日学习总结
  • 疯狂前端面试题(四)
  • YOLOv11-ultralytics-8.3.67部分代码阅读笔记-metrics.py
  • SuperCopy解除网页禁用复制功能插件安装和使用
  • UP-VLA:具身智体的统一理解与预测模型
  • Unity 基于状态机的逻辑控制详解
  • 傅里叶单像素成像技术研究进展
  • IDEA接入DeepSeek
  • 前端如何判断浏览器 AdBlock/AdBlock Plus(最新版)广告屏蔽插件已开启拦截
  • macOS 上部署 RAGFlow