C++实现简单的垃圾回收RAII方案 _ 计数指针原理【源码】
C++ 实现智能指针:基于引用计数的 RAII 垃圾回收方案【完整源码解析】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
原生指针的缺陷:为何手动管理内存易导致双重释放与悬垂指针
在 C++ 中直接使用原始指针(raw pointer)配合 delete 进行内存管理,本质上是将“资源由谁释放”以及“何时释放”的决策责任完全交由开发者承担。当对象被多个模块共享,或程序因异常提前退出作用域时,关键的释放操作极易被遗漏、重复执行,甚至在对象已销毁后仍被访问,从而引发内存泄漏、双重释放(double-free)或悬垂指针(dangling pointer)问题。需要明确的是:RAII(资源获取即初始化)机制本身主要解决的是单作用域内的资源自动释放,而像 std::shared_ptr 这类基于引用计数的智能指针,才是对 RAII 理念的扩展——它将“引用计数”这一状态本身也纳入了资源管理的范畴。
在实际编程中,以下几类错误尤为常见:
- 函数返回了在局部通过
new分配的对象指针,而调用方忘记调用delete,导致内存泄漏。 - 两个
std::shared_ptr虽然指向同一块原始内存,但却分别通过独立的new操作构造(非同一来源),导致引用计数信息错乱,最终引发双重释放。 - 循环引用问题,例如对象 A 持有对象 B 的
std::shared_ptr,同时对象 B 也持有对象 A 的std::shared_ptr,致使引用计数永不为零,对象无法被正确析构。
动手实现一个基础版 ref_ptr:非线程安全的引用计数指针
要实现一个最小可用的引用计数智能指针,其核心离不开三个要素:指向目标对象的原始指针、一个独立的引用计数器、以及封装在析构函数中的资源释放逻辑。这里有几点关键设计原则:计数器不应放在被管理对象内部(那属于侵入式方案,通用性差),也不能与对象共用同一块内存(除非定制 operator new),否则在 delete 对象时,计数器自身也将无法被安全释放。
具体实现时,需关注以下要点:
立即学习“C++免费学习笔记(深入)”;
- 计数器需动态分配:应使用
new size_t(1)来创建计数器,确保其生命周期独立于所托管的对象。 - 正确实现拷贝与赋值:执行拷贝构造或赋值操作时,需先对原指针的计数器执行
--(*cnt)并检查是否归零,以决定是否释放旧资源,然后再关联新资源并增加其计数。 - 移动语义需妥善处理:实现移动构造函数和移动赋值运算符时,必须将源对象(右值)的内部指针和计数器置为
nullptr,防止其析构函数误释放资源。 - 保持接口行为一致性:应提供
get()、operator->、operator*等接口,其行为需与std::shared_ptr保持一致,以降低使用者的学习成本。
以下是一个简化版的核心实现代码示例:
templateclass ref_ptr { T* ptr_; size_t* cnt_; public: explicit ref_ptr(T* p = nullptr) : ptr_(p), cnt_(p ? new size_t(1) : nullptr) {} ref_ptr(const ref_ptr& other) : ptr_(other.ptr_), cnt_(other.cnt_) { if (cnt_) ++(*cnt_); } ~ref_ptr() { if (cnt_ && --(*cnt_) == 0) { delete ptr_; delete cnt_; } } T* get() const { return ptr_; } T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } };
关键行为对比:手写 ref_ptr 与 std::shared_ptr 在 reset 和 release 上的差异
标准库中的 std::shared_ptr::reset() 设计得非常周全:它会先减少当前托管资源的引用计数,并据此判断是否需要释放,然后再去接管新的对象。而手写版本如果未实现类似逻辑,直接进行类似 ptr_ = new_obj; cnt_ = new_cnt; 的赋值,就会跳过对旧资源的清理步骤,从而导致内存泄漏。
在实现这些功能时,开发者常会遇到以下几个陷阱:
- 误用赋值替代
reset():若未实现专门的reset()方法,而试图用拷贝赋值来替代,可能会引发不必要的临时对象构造与析构,不仅影响性能,还会使引用计数的管理逻辑变得异常复杂。 - 实现
reset(nullptr)时遗漏计数器置空:这会导致后续析构函数仍尝试对已置空的cnt_进行delete操作,从而访问野指针,引发未定义行为。 - 错误地提供
release()方法:对于引用计数型智能指针,其语义并不支持“交出所有权但不减少引用计数”的操作,这会破坏 RAII 自动管理生命周期的核心契约,因此通常不应提供此方法。 - 缺乏自定义删除器支持:简易实现往往不支持传入自定义删除器(
Deleter),因此无法灵活管理那些非new分配(如malloc)或需要特殊释放函数(如CloseHandle)的资源。
破解循环引用:必须依赖 std::weak_ptr 吗?手写弱引用实现思路
弱引用指针(weak_ptr)并非另一个拥有所有权的指针,它本质上是对同一控制块(包含引用计数器)的**非拥有式观察者**。它不参与强引用计数的增减,仅在需要时尝试“升级”为强引用(即检查目标对象是否依然存活)。因此,要实现弱引用,必须在控制块中额外维护一个“弱引用计数”字段,用以记录当前有多少个 weak_ptr 正在观察此控制块。
若要手动实现弱引用功能,必须把握以下几个核心要点:
- 扩展控制块结构:引用计数器需从单一的
size_t扩展为至少包含strong_count(强引用计数)和weak_count(弱引用计数)两个字段的结构体。 - 分离析构逻辑:
weak_ptr析构时,只减少weak_count;只有当strong_count == 0且weak_count == 0两个条件同时满足时,才能安全地释放控制块内存本身。 - 安全实现“升级”操作:
lock()方法需要原子性地读取strong_count并判断是否大于 0。只有在对象存活时,才构造一个新的强引用指针(此时会增加strong_count)。 - 明确并发限制:即使实现的是非线程安全版本,也必须在文档或代码中明确标注“不保证多线程并发调用的安全性”,防止使用者误将其用于异步场景,导致难以调试的竞态条件问题。
实现到此阶段,其复杂度已接近 std::shared_ptr 标准实现的下限。对于绝大多数生产级项目而言,直接使用 C++ 标准库提供的智能指针是更稳健、高效的选择。自己动手实现引用计数指针,其主要价值在于深入理解其底层原理与设计思想,或用于某些无法使用标准模板库(STL)的特定受限环境,而非为了替代标准库。
相关攻略
C++如何解析MPEG-TS流中的PAT与PMT节目表【深度】 PAT表是解析MPEG-TS流的关键起点,它固定位于PID为0x0000的TS包中。解析时需通过payload_unit_start_indicator标志定位新表起始,正确处理adaptation field以找到payload,校验
C++ std::identity用法详解:函数对象占位符与ranges算法核心指南 std::identity 核心概念与应用场景解析 在C++20标准库中,std::identity绝非简单的语法糖,而是std::ranges算法体系中表达“元素原样透传”意图的唯一标准函数对象。当你调用std:
std::is_base_of编译期报错解析:非法类型、不完整类型与非类类型传入的应对方案 std::is_base_of 编译期报错的根本原因 许多C++开发者在首次使用 std::is_base_of 模板时,常对其在编译阶段直接报错感到困惑。这源于其作为类型特征(type trait)的本质—
Linux下birth time仅能通过statx()读取且不可设置,需内核≥4 11、支持的文件系统及正确挂载选项;glibc未暴露该字段,stat()等传统接口无法获取。 Linux 下用 stat 和 utimensat 读取 设置 birth time(创建时间) 在Linux的世界里,文件
cista 实现微秒级序列化的核心原理:零开销内存拷贝与偏移重定位 cista 微秒级序列化的技术实现解析 cista 之所以能够实现微秒甚至纳秒级的序列化性能,源于其颠覆性的设计理念。与传统的序列化方案不同,cista 彻底摒弃了运行时类型识别(RTTI)、动态反射和堆内存分配等重型操作。它采用了
热门专题
热门推荐
iPhone 17:为何成为苹果史上最长寿的爆款? 最近科技圈有个消息传得挺热:iPhone 17标准版的生产周期被大幅拉长了。这可不是简单的产能调整,背后是苹果近期完成的大规模产能扩展。看来,这款热门机型已经瞄准了今年下半年的双11战场,准备再掀一波销售热潮。 消息一出,不少网友都在猜测原因。矛头
在快节奏的都市生活中,一款兼具便携性与环保特性的出行工具正成为越来越多人的选择 城市通勤的“最后一公里”难题,催生了对灵活出行方案的持续探索。近期,小米有品推出的mini智能电动平衡车,以其独特的设计理念和深度智能化功能,迅速吸引了市场的目光。它不仅仅是一款酷玩装备,更切实地为青少年和上班族提供了高
在数字化教育蓬勃发展的当下,家长们为孩子挑选学习设备时,既希望设备具备护眼功能,又期望能满足多样化的学习需求。传统平板电脑功能虽丰富,但长时间使用易引发视力疲劳;普通学习机功能又相对单一,难以契合现代教育的发展趋势。在此背景下,科大讯飞AI学习机系列凭借先进的护眼技术与智能学习系统,成为众多家长和学
目录 ethzilla是谁? ETHZilla独特其他ETH DAT之处 1、Peter Thiel持股ETHZilla近30% 2、Vitalik和以太坊基金会入局 3、聚焦DeFi和链上策略 结语 以太坊财库概念的热度,最近真是肉眼可见。伴随着这股热潮,ETH价格也强势突破了4700美元,距离历
全球彩电市场:存量博弈下的冰与火之歌 最近,行业调研机构奥维睿沃(A VC Revo)发布了一份引人关注的报告,揭示了2025年全球彩电市场的真实图景。数据显示,全球彩电整体出货量达到2 64亿台,同比仅微跌0 1%,市场基本盘看似稳固。 然而,拆开来看,内部结构正在发生深刻变化。LCD液晶电视依然





