全面理解-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.rhkb.cn/news/15960.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025 年 2 月 TIOBE 指数

2025 年 2 月 TIOBE 指数 二月头条:快,更快,最快! 现在,世界需要每秒处理越来越多的数字,而硬件的发展速度却不够快,程序的速度变得越来越重要。话虽如此,快速编程语言在 TIOBE 指数中取得进展也就不足为奇了。编程语言 C++ 最近攀升至第 2 位,Go 已稳居前 10 名,Ru…

【Flink源码分析】5. Flink1.19源码分析-异步编程(CompletableFuture)

5 CompletableFuture 实现异步编排&#xff1b;获取异步任务执行的结果。 CompletableFuture提供了几十种方法&#xff0c;辅助我们的异步任务场景。这些方法包括创建异步任务、异步任务回调、多个任务组合处理等方面。 5.1 supplyAsync 方法 supplyAsync 执行 Completable…

LabVIEW在呼吸机测试气体容量计算

在呼吸机测试中&#xff0c;精确测量气体容量变化是评估设备性能的关键步骤。通过监测呼吸机气道内的压力变化&#xff0c;并结合流阻和肺顺应性等参数&#xff0c;可以计算出单位时间内的气体容量变化。本案例基于LabVIEW实现该计算过程&#xff0c;以确保测试数据的准确性和一…

Lombok使用指南

引言 lombok作为减少我们代码量的利器&#xff0c;本文将列举常用的几个注解&#xff0c;来帮助减少代码量 注解及其功能 Getter 和 Setter import lombok.Getter; import lombok.Setter;Getter Setter public class Person {private String name;private int age; } …

JAVA学习第一天

String的构造方法-118 String创建对象的特点——119 String字符串的比较——120 字符串的遍历——122 两个函数&#xff1a;length&#xff08;&#xff09;&#xff0c;charAt&#xff08;&#xff09; StringBuilder——127 String的内容是不可变的 StringBuilder的内容是可变…

sqli-lab靶场学习(六)——Less18-22(User-Agent、Referer、Cookie注入)

前言 前面的关卡&#xff0c;都是直接在输入框或者浏览器的地址栏上做文章即可。但本文这几关&#xff0c;需要用工具拦截请求修改请求头部才行。 Less18&#xff08;User-Agent注入&#xff09; 本关的注入点在User-Agent。我们在用户名和密码框中输入admin/admin后&#xf…

Spring依赖注入方式

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…

arcgis界址点编号工具开发原理(西北角顺时针)

arcgis界址点编号工具开发原理&#xff08;西北角顺时针&#xff09; 1、工具实现思路。寻找离包络矩形左顶角最近的点作为起点。如下图&#xff1a;距离包络矩形左顶角最近的点&#xff0c;作为J1点没有任何问题。 问题在于并不是所有的地块&#xff0c;都这么中规中矩、合情…

分布式服务框架 如何设计一个更合理的协议

1、概述 前面我们聊了如何设计一款分布式服务框架的问题&#xff0c;并且编码实现了一个简单的分布式服务框架 cheese, 目前 cheese 基本具备分布式服务框架的基本功能。后面我们又引入了缓存机制&#xff0c;以及使用Socket替代了最开始的 RestTemplate。并且还学习了网络相关…

生信云服务器:让生物信息学分析更高效、更简单【附带西柚云优惠码】

随着生物信息学的快速发展&#xff0c;基因组测序、单细胞分析等复杂任务逐渐成为研究者们的日常工作。然而&#xff0c;个人电脑在处理这些任务时往往面临性能瓶颈&#xff0c;如内存不足、运算速度慢等问题&#xff0c;导致分析任务频繁失败或崩溃。为了解决这一难题&#xf…

[AUTOSAR通信] - PDUR模块解读

点击订阅专栏不迷路 文章目录 一、 PDUR模块概述二、功能描述2.1 发送路由功能2.2 接收路由功能2.3 网关路由功能2.4 路由控制功能 三、配置項介紹3.1. PduRBswModules3.2. PduRGeneral3.3. PduRRoutingTables3.4. PduRRoutingPath3.5. PduRSrcPdu3.6. PduRDestPdu 四、总结 &g…

分治下的快速排序(典型算法思想)—— OJ例题算法解析思路

目录 一、75. 颜色分类 - 力扣(LeetCode) 运行代码: 一、算法核心思想 二、指针语义与分区逻辑 三、操作流程详解 四、数学正确性证明 五、实例推演(数组[2,0,2,1,1,0]) 六、工程实践优势 七、对比传统实现 八、潜在问题与解决方案 九、性能测试数据 十、扩展…

分层耦合 - IOC详解

推荐使用下面三种, 第一种多用于其他类 声明bean的时候&#xff0c;可以通过value属性指定bean的名字&#xff0c;如果没有指定&#xff0c;默认为类名首字母小写。 使用以上四个注解都可以声明bean&#xff0c;但是在springboot集成web开发中&#xff0c;声明控制器bean只能用…

PDF Shaper:免费多功能 PDF 工具箱,一站式满足您的 PDF 需求!

​PDF Shaper 是一款功能强大且完全免费的 PDF 工具箱&#xff0c;它几乎涵盖了日常 PDF 操作的方方面面&#xff0c;无论是转换、编辑还是处理&#xff0c;都能轻松搞定。以下是这款软件的详细介绍&#xff1a; 功能丰富&#xff0c;一应俱全 PDF 转换功能强大 PDF 转 Word&am…

未来替代手机的产品,而非手机的本身

替代手机的产品包括以下几种&#xff1a; 可穿戴设备&#xff1a;智能手表、智能眼镜等可穿戴设备可以提供类似手机的功能&#xff0c;如通话、信息推送、浏览网页等。 虚拟现实&#xff08;VR&#xff09;技术&#xff1a;通过佩戴VR头显&#xff0c;用户可以进行语音通话、发…

deepseek+“D-id”或“即梦AI”快速生成短视频

1、deepseek生成视频脚本 1.1、第一步&#xff1a;使用通用模板提出需求&#xff0c;生成视频脚本 对话输入示例脚本1&#xff1a; 大年初五是迎财神的日志&#xff0c;帮我生成10秒左右的短视频&#xff0c; 体现一家3口在院子里欢庆新年&#xff0c; 孩子在院子里放鞭炮烟…

在CT107D单片机综合训练平台上实现外部中断控制LED闪烁

引言 在单片机开发中&#xff0c;外部中断是一个非常重要的功能&#xff0c;它可以让单片机在检测到外部信号变化时立即做出响应。本文将详细介绍如何在CT107D单片机综合训练平台上使用外部中断来控制LED灯的闪烁。我们将使用两种不同的方式来实现这一功能&#xff1a;一种是在…

为什么推荐使用 LabVIEW 开发

在仪器行业的软件开发中&#xff0c;LabVIEW 以其图形化编程、快速原型开发、高效硬件集成的优势&#xff0c;成为自动化测试和控制系统的理想选择。尽管一些工程师仍然坚持使用 C 语言&#xff0c;但这更多是出于习惯&#xff0c;而非技术上的必然。LabVIEW 不仅支持 NI 硬件&…

力扣1448. 统计二叉树中好节点的数目

Problem: 1448. 统计二叉树中好节点的数目 文章目录 题目描述思路复杂度Code 题目描述 思路 对二叉树进行先序遍历&#xff0c;边遍历边对比并更新当前路径上的最大值pathMax&#xff0c;若当pathMax小于等于当前节点值&#xff0c;则好节点的数目加一 复杂度 时间复杂度: O (…

DeepSeek帮助做【真】软件需求-而不是批量刷废话

尝试给DeepSeek一份系统用例规约&#xff0c;让它帮判断哪些地方还没有覆盖涉众利益。结果见以下 需求工作的重点可以放在建模精细的真实现状流程和精细的真实涉众利益上&#xff0c;AI帮助推演系统需求。