news 2026/4/15 10:52:45

int * 代替外部计数结构体实现SharedPtr

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
int * 代替外部计数结构体实现SharedPtr

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

    • 一、核心矛盾点
    • 二、错误实现演示与问题分析
      • 错误代码片段
      • 测试场景与崩溃后果
      • 核心问题总结
    • 三、正确方案的本质:用指针共享堆上的计数
      • 简化优化:不用结构体,只用 `int*`
    • 四、补充解答:结构体是必须的吗?
    • 五、总结

我有一个疑问RefCount必须以一个类或者结构体放置在外面吗,这里面本质不就是一个计数吗,能不能把这个计算int类型变量直接放置在sharedptr类中作为成员变量呢,请说明行或者不行及其原因

结论先行:绝对不行,不能将引用计数直接作为SharedPtr的普通int成员变量,这违背了shared_ptr共享计数的核心设计原理。下面结合底层逻辑、错误案例、后果分析,为你详细解释原因,同时给出简化的正确实现方案。


一、核心矛盾点

shared_ptr的设计目标:多个智能指针对象共享同一个资源,且共用同一个引用计数,计数的增减对所有共享该资源的指针都生效。

  • 类的普通成员变量(值类型):属于对象独占,每个SharedPtr实例都会独立拥有一份成员变量副本,彼此隔离、无法同步;
  • 引用计数的需求:必须是全局唯一、所有共享实例都能访问修改的共享变量。

这是直接用int成员变量无法解决的本质矛盾。


二、错误实现演示与问题分析

我们模拟你设想的写法,把计数直接作为成员变量,看看会发生什么致命问题:

错误代码片段

template<typenameT>classBadSharedPtr{private:T*ptr_;intref_count_;// 直接将计数作为普通成员变量(错误写法)public:// 构造函数explicitBadSharedPtr(T*p=nullptr):ptr_(p),ref_count_(0){if(ptr_)ref_count_=1;}// 拷贝构造:拷贝指针和计数值BadSharedPtr(constBadSharedPtr&other){ptr_=other.ptr_;ref_count_=other.ref_count_;ref_count_++;// 尝试给当前对象的计数+1}// 析构函数~BadSharedPtr(){ref_count_--;if(ref_count_==0){deleteptr_;std::cout<<"资源释放\n";}}T&operator*(){return*ptr_;}intuse_count()const{returnref_count_;}};

测试场景与崩溃后果

voidtest(){BadSharedPtr<int>sp1(newint(10));// sp1.ptr_ → 堆内存,sp1.ref_count_ = 1BadSharedPtr<int>sp2=sp1;// 拷贝构造:sp2.ptr_ 和 sp1 指向同一块内存// sp2.ref_count_ = 2,sp1.ref_count_ 仍然是 1(两个独立变量!)std::cout<<sp1.use_count();// 输出 1std::cout<<sp2.use_count();// 输出 2}// 函数退出,析构顺序:sp2 先析构 → sp2.ref_count=1,不释放资源// sp1 后析构 → sp1.ref_count=0,释放资源 ✔️ 看似没问题?// 进阶场景:多一层拷贝BadSharedPtr<int>sp3=sp2;// 析构时:sp3计数减到1、sp2计数减到1、sp1计数减到0 → 仅释放一次// 但如果执行 reset、赋值操作,会直接崩溃:sp1.reset();// sp1 计数减为0,释放内存;但 sp2/sp3 仍持有该内存指针,变成野指针!// sp2 析构时会再次 delete 同一块内存 → **重复释放,程序崩溃**

核心问题总结

  1. 计数隔离,无法同步:每个对象的ref_count_都是独立副本,修改一个不会影响其他对象,计数彻底失效;
  2. 重复释放/内存泄漏:无法准确判断资源的真实引用数量,要么重复释放内存导致崩溃,要么计数永远不为0导致内存泄漏;
  3. 行为不可控:赋值、reset、移动等操作都会产生未定义行为。

三、正确方案的本质:用指针共享堆上的计数

要实现唯一且共享的计数,必须将计数对象分配在堆内存上,所有SharedPtr实例只存储一个指针,指向这块堆内存。
所有实例通过指针访问同一个计数变量,修改后全局同步,这也是我们最初用RefCount结构体/int*的核心原因。

简化优化:不用结构体,只用int*

你之前看到的RefCount结构体是为了扩展(兼容弱引用计数),如果仅实现基础共享计数,完全可以去掉结构体,直接用int*作为成员变量,这是最精简的正确写法:

#include<iostream>#include<utility>#include<string>template<typenameT>classSharedPtr{private:T*ptr_;// 托管资源指针int*ref_cnt_;// 指针:指向堆上的唯一引用计数(核心修正)// 私有释放逻辑voidrelease(){if(ref_cnt_){--(*ref_cnt_);// 计数为0,释放资源和计数对象if(*ref_cnt_==0){deleteptr_;deleteref_cnt_;}// 清空当前对象指针ptr_=nullptr;ref_cnt_=nullptr;}}public:// 空构造SharedPtr():ptr_(nullptr),ref_cnt_(nullptr){}// 有参构造explicitSharedPtr(T*p):ptr_(p){if(ptr_){ref_cnt_=newint(1);// 堆上分配计数,初始值1}else{ref_cnt_=nullptr;}}// 拷贝构造:共享指针和计数,计数+1SharedPtr(constSharedPtr&other){ptr_=other.ptr_;ref_cnt_=other.ref_cnt_;// 共享同一个计数指针if(ref_cnt_){++(*ref_cnt_);}}// 移动构造:转移所有权,不修改计数SharedPtr(SharedPtr&&other)noexcept{ptr_=other.ptr_;ref_cnt_=other.ref_cnt_;other.ptr_=nullptr;other.ref_cnt_=nullptr;}// 拷贝赋值SharedPtr&operator=(constSharedPtr&other){if(this!=&other){release();ptr_=other.ptr_;ref_cnt_=other.ref_cnt_;if(ref_cnt_)++(*ref_cnt_);}return*this;}// 析构函数~SharedPtr(){release();}// 工具函数T&operator*()const{return*ptr_;}T*operator->()const{returnptr_;}intuse_count()const{returnref_cnt_?*ref_cnt_:0;}voidreset(T*p=nullptr){release();ptr_=p;if(ptr_)ref_cnt_=newint(1);}};

四、补充解答:结构体是必须的吗?

不是必须的!

  • 基础场景:用int*就足够实现共享计数;
  • 扩展场景:需要弱引用计数、自定义删除器、原子操作标记等附加信息时,用结构体封装多个变量更优雅(这也是标准库和我们最初代码用RefCount的原因);
  • 核心约束不变:计数必须存储在堆上,通过指针共享,不能是类的值类型成员

五、总结

  1. 不行的根本原因:普通int成员变量是对象独占的,无法让多个SharedPtr共享同一个计数,会导致计数失效、内存崩溃;
  2. 正确实现规则:引用计数必须分配在堆内存中,所有智能指针对象通过指针共享这块内存,保证计数全局同步;
  3. 简化写法:无需结构体,用int*即可实现基础功能,结构体仅用于代码扩展和规范化管理。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 9:43:37

基于stm32单片机智能无线家用插座

基于STM32单片机的智能无线家用插座设计 第一章 绪论 传统家用插座仅具备电力通断的基础功能&#xff0c;存在用电安全防护不足、无法远程控制、能耗监测缺失等问题&#xff0c;难以适配智能家居场景下的精细化用电管理需求。STM32单片机凭借低功耗、高集成度、丰富的无线通信接…

作者头像 李华
网站建设 2026/4/10 18:16:35

矢量网络分析仪与频谱分析仪:射频测量领域的双璧

在射频微波测试领域&#xff0c;矢量网络分析仪&#xff08;VNA&#xff09;与频谱分析仪&#xff08;SA&#xff09;如同两把精密的钥匙&#xff0c;分别开启着信号特性探索的不同维度。前者以相位测量为核心&#xff0c;后者聚焦频谱解析&#xff0c;二者在通信、雷达、电子对…

作者头像 李华
网站建设 2026/4/15 4:08:26

python+vue开发斗南花卉鲜花商城管理信息系统 采购 财务 功能全

文章目录系统概述核心功能模块技术实现要点扩展功能大数据系统开发流程主要运用技术介绍源码文档获取定制开发/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统概述 基于Python&#xff08;后端&#xff09;和Vue.js&#xff08;前端&#xff09…

作者头像 李华
网站建设 2026/4/13 9:33:23

python+vue开发网上电子书店商城好书推荐管理系统 论坛-pycharm DJANGO FLASK

文章目录技术栈选择系统功能模块开发流程关键代码示例算法实现大数据系统开发流程主要运用技术介绍源码文档获取定制开发/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;技术栈选择 Python后端框架可选Django或Flask。Django适合快速构建全功能网站…

作者头像 李华
网站建设 2026/4/4 0:31:05

vue+python 的宠物领养管理系统沙箱支付-pycharm DJANGO FLASK

文章目录 技术栈概述核心功能模块支付集成示例&#xff08;Flask 支付宝沙箱&#xff09;部署与调试注意事项 大数据系统开发流程主要运用技术介绍源码文档获取定制开发/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 技术栈概述 Vue.js Python …

作者头像 李华