C++ shared_ptr怎么工作 引用计数机制解析

shared_ptr通过引用计数管理对象生命周期,多个指针共享同一控制块,引用计数为0时自动释放资源;使用make_shared提升性能,避免裸指针重复构造导致多控制块;循环引用需用weak_ptr打破,确保内存安全释放。

c++ shared_ptr怎么工作 引用计数机制解析

在C++中,shared_ptr 是一种智能指针,用于管理动态分配的对象,确保对象在不再被使用时自动释放。它通过引用计数机制实现共享所有权,多个 shared_ptr 可以指向同一个对象,只有当最后一个指向该对象的 shared_ptr 被销毁或重置时,对象才会被删除。

引用计数的基本原理

shared_ptr 内部维护两个指针:一个指向实际管理的对象,另一个指向控制块(control block)。控制块中包含引用计数、弱引用计数以及可能的自定义删除器和分配器。

每当一个新的 shared_ptr 通过拷贝构造或赋值操作共享同一个对象时,引用计数加1。当某个 shared_ptr 被销毁或重新赋值时,引用计数减1。当引用计数变为0时,说明没有 shared_ptr 再指向该对象,系统会自动调用删除器释放对象内存。

例如:

立即学习“C++免费学习笔记(深入)”;

shared_ptr ptr1 = make_shared(42);
shared_ptr ptr2 = ptr1; // 引用计数变为2
ptr1.reset(); // 引用计数减为1,对象未释放
ptr2.reset(); // 引用计数减为0,对象被删除

控制块的创建时机

控制块并不是在所有情况下都立即创建。它的创建遵循以下规则:

使用 make_shared 时,对象和控制块在同一个内存块中分配,效率更高。 通过裸指针构造 shared_ptr 时,会单独分配控制块。 从 weak_ptr 提升为 shared_ptr 时,如果对象还活着,会复用已有的控制块。

重要的是,同一个对象不应被多个独立的裸指针分别构造 shared_ptr,否则会导致多个控制块,引发重复释放或计数错误。

循环引用问题与 weak_ptr

当两个对象通过 shared_ptr 相互持有对方时,引用计数永远不会归零,造成内存泄漏。这种情况称为循环引用。

解决方案是使用 weak_ptr。它不增加引用计数,只观察对象是否存在。在需要访问对象时,可尝试提升为 shared_ptr,如果对象已释放,提升会失败。

例如:

立即学习“C++免费学习笔记(深入)”;

struct Node {
shared_ptr next;
weak_ptr prev; // 避免循环引用
};

基本上就这些。理解 shared_ptr 的引用计数机制,关键在于掌握控制块的生命周期和共享语义。正确使用能极大提升代码安全性和可维护性。

以上就是C++ shared_ptr怎么工作 引用计数机制解析的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1472480.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 19:39:32
下一篇 2025年12月18日 19:39:50

相关推荐

  • C++引用和指针区别 引用特性与使用场景

    引用不占用额外内存且不能为空,指针需存储地址并可为空;引用用于安全高效的参数传递,指针适用于动态内存管理和复杂数据结构。 C++中,引用和指针都可以间接访问另一个变量,但它们本质上是不同的。引用更像变量的别名,一旦绑定就不能改变,而指针则是一个存储变量地址的变量,可以重新赋值指向不同的地址。选择使用…

    2025年12月18日
    000
  • C++异常安全swap 强异常安全实现

    强异常安全的swap通过拷贝和交换实现,先复制可能抛出异常,swap本身用noexcept交换指针和大小,确保赋值要么成功要么无影响。 在C++中,实现一个强异常安全的 swap 函数是确保资源管理类在异常发生时仍能保持对象状态一致的关键。强异常安全保证:如果操作抛出异常,程序状态回滚到操作前的状态…

    2025年12月18日
    000
  • C++模板别名定义 using简化复杂类型名

    使用using定义模板别名可显著提升C++代码的可读性和维护性,解决复杂类型冗长、维护困难及模板元编程中的类型操作难题,相比typedef具有语法统一、支持模板参数等优势,适用于简化嵌套类型、封装接口和构建领域语义类型。 C++中, using 关键字在模板别名定义上的应用,无疑是现代C++简化复杂…

    2025年12月18日
    000
  • C++图片转ASCII art 像素灰度转换方法

    核心是将像素亮度映射为字符。先用加权平均法计算灰度值(gray = 0.299×R + 0.587×G + 0.114×B),再将0~255灰度归一化到字符集长度,如’@#%$*+=-:. ‘共11级,index = gray×10/255取对应字符。因字符高大于宽,需将图像…

    2025年12月18日
    000
  • C++文件共享读写 多进程访问控制

    答案是使用文件锁机制。多进程环境下需通过文件锁协调读写,避免数据错乱,C++需依赖系统API实现。 多进程环境下对同一文件进行读写,这事儿听起来简单,实则是个经典的并发控制难题。如果不加以妥善管理,数据错乱、文件损坏那是分分钟的事。核心要义在于,我们必须得有一种机制,让各个进程能“协商”好,谁在什么…

    2025年12月18日
    000
  • C++异常处理机制 try catch throw基本结构

    C++异常处理通过try、catch、throw实现;2. try块包裹可能出错代码,如年龄为负时抛出异常;3. throw抛出异常后由匹配的catch块捕获处理,避免程序崩溃,提升健壮性。 在C++中,异常处理机制通过 try、catch 和 throw 三个关键字实现,用于在程序运行时检测和响应…

    2025年12月18日
    000
  • C++智能指针限制 不适用场景分析

    智能指针虽能有效管理内存,但在循环引用、性能敏感场景、与C风格API交互、数组及非标准内存管理、生命周期明确时存在局限,需谨慎使用。 智能指针是C++中管理动态内存的重要工具,能有效减少内存泄漏和资源管理错误。但它们并非万能,某些场景下使用反而会带来问题或不必要开销。以下是智能指针的常见限制及其不适…

    2025年12月18日
    000
  • C++数组如何声明和初始化 静态数组定义与初始化方法

    静态数组在c++++中通过编译时常量指定大小,可在声明时用初始化列表、花括号语法或自动推导大小的方式进行初始化,未初始化的局部数组值为未定义,全局或静态数组自动初始化为0,多维数组按行优先存储,现代c++推荐使用std::array替代原生数组以提升安全性,但原生静态数组仍在性能敏感场景广泛使用。 …

    2025年12月18日
    000
  • C++内存顺序约束 多线程操作可见性

    C++内存顺序通过原子操作和内存序约束(如memory_order_release/acquire)确保多线程间操作的可见性与顺序性,防止因编译器或CPU重排导致的数据竞争;其中relaxed仅保证原子性,acquire-release建立跨线程“发生前”关系,而seq_cst提供全局顺序一致性但性…

    2025年12月18日
    000
  • STL关联容器查找效率怎么优化 unordered_map与map选择标准

    在c++++开发中,选择unordered_map还是map取决于具体使用场景。一、unordered_map基于哈希表实现,查找时间复杂度为o(1),适合频繁查找且无需排序的场景;而map基于红黑树实现,查找复杂度为o(log n),适合需要键排序、范围查找或稳定迭代器的场景。二、当需要有序遍历或…

    2025年12月18日 好文分享
    000
  • C++结构体反射实现 成员遍历与访问技术

    C++原生不支持反射因设计哲学侧重性能,需通过宏元编程或库实现伪反射,如用宏注册成员生成元数据,结合offsetof和typeid实现遍历与安全访问,但存在维护成本高、类型安全需手动校验等局限,未来标准或引入原生反射。 C++中实现结构体成员的反射与遍历,通常并不是语言原生支持的特性,这确实是C++…

    2025年12月18日
    000
  • 类型推导auto怎么用 模板函数返回值类型推断

    auto类型推导由编译器自动确定变量类型,简化复杂类型声明,提升代码可读性与维护性,尤其适用于迭代器、lambda表达式及模板函数返回类型;C++14起支持auto作为函数返回类型,decltype(auto)可保留引用和const属性,避免类型推导偏差;需注意auto忽略顶层const与引用、初始…

    2025年12月18日
    000
  • C++文件压缩工具 基础压缩算法实践

    RLE压缩通过记录连续相同字节的重复次数实现数据压缩。程序先读取输入文件并统计相邻相同字节的数量,当字节变化或计数达255时,将计数值和对应字节写入输出文件;解压时读取每对计数与字节,重复写入相应次数。该方法适用于重复数据多的场景,但对随机数据可能增加体积,且需以二进制模式操作文件以避免格式转换。 …

    2025年12月18日
    000
  • 文件缓冲区有什么作用 flush同步缓冲区时机选择

    文件缓冲区通过减少磁盘I/O次数提升性能,但数据滞留内存存在丢失风险,因此需权衡flush时机以平衡性能与安全。 文件缓冲区就像是程序和硬盘之间的一个小小的中转站,一个内存里的临时存放区。它最核心的作用,就是用来弥补CPU和内存(速度飞快)与磁盘(慢悠悠)之间的巨大速度差异。说白了,就是为了减少直接…

    2025年12月18日
    000
  • C++异常替代方案 错误码optional对比

    错误码性能优但易忽略,std::optional语义清晰难忽略但无错误信息,std::expected兼顾两者,项目应统一错误处理方式。 在C++中处理错误,异常(exceptions)是一种常见方式,但并不是唯一选择。很多项目出于性能、可预测性或嵌入式环境限制等原因,会选择禁用异常。这时,错误码和…

    2025年12月18日
    000
  • C++函数模板怎么定义 类型参数化实现方法

    C++函数模板通过template将类型参数化,使同一函数逻辑适用于多种类型,编译时根据实参类型推导并实例化具体函数版本,如add(5,3)生成int版本,add(3.14,2.71)生成double版本,实现代码复用;为解决通用逻辑不适用的特殊情况,可对特定类型全特化,如为const char*提…

    2025年12月18日
    000
  • C++ unique_ptr用法 独占所有权指针实现

    unique_ptr是C++11引入的独占式智能指针,通过移动语义转移所有权,防止内存泄漏,推荐使用make_unique创建实例。 unique_ptr 是 C++11 引入的一种智能指针,用于管理动态分配的对象,确保同一时间只有一个指针拥有该对象的所有权。当 unique_ptr 被销毁时,它所…

    2025年12月18日
    000
  • C++字面量操作符 自定义类型后缀

    C++自定义字面量操作符通过定义以_开头的后缀(如_m、_cm),将带单位的字面量直接转换为自定义类型对象,提升代码可读性与类型安全性。核心是实现operator””后缀函数,支持整数(unsigned long long)、浮点(long double)和字符串(const…

    2025年12月18日
    000
  • C++构造函数异常 对象构造失败处理

    构造函数抛出异常时,对象未完全构造,析构函数不会被调用,已构造的成员变量和基类按逆序自动析构,确保资源释放;应使用RAII(如智能指针)管理资源,避免泄漏;可通过函数try块捕获成员或基类异常并转换异常类型;设计上建议将可能失败的操作移至初始化函数,采用两段式构造,提升异常安全性。 当C++对象在构…

    2025年12月18日
    000
  • C++内存映射文件 大文件高效访问技术

    答案:内存映射文件通过将文件直接映射到进程地址空间,避免传统I/O的数据拷贝开销,适用于大文件处理、随机读写、多进程共享等场景;在C++中,Windows使用CreateFileMapping和MapViewOfFile,Linux使用mmap实现;需注意跨平台差异、页面错误、虚拟内存消耗及多线程/…

    2025年12月18日
    000

发表回复

登录后才能评论
关注微信