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

《Unreal 对 C++ 做了什么》系列 11. 弱引用与软引用:TWeakObjectPtr 和 TSoftObjectPtr

《Unreal 对 C++ 做了什么》系列 (11/54)

11. 弱引用与软引用:TWeakObjectPtr 和 TSoftObjectPtr 🔗

🚀 导言:打破“强引用”的垄断

在标准 C++ 中,std::weak_ptr必须配合std::shared_ptr。而在虚幻引擎中,所有UObject的生死由 GC 说了算。

如果我们只使用UPROPERTY标记的原始指针(强引用),会面临两个严峻问题:

  1. 循环引用(Circular Reference):两个对象互相强引用,导致 GC 永远认为它们“可达”,内存永不释放。
  2. 加载链爆炸(Reference Loading Hell):强引用会导致资源被“打包加载”。你引用了一个敌人,引擎会自动把该敌人引用的武器、特效、甚至音效全部塞进内存。

为了解决这些问题,UE 构建了TWeakObjectPtrTSoftObjectPtr


🔑 1. TWeakObjectPtr:安全的“旁观者”

TWeakObjectPtr是一种不参与 GC 追踪的弱引用。它允许你引用一个对象,但不会阻止该对象被回收。

底层黑科技:它是如何感知“死亡”的?

它内部存储的不是内存地址,而是InternalIndex(对象索引)SerialNumber(序列号)

  • 当你通过.Get()访问时,它会去全局对象表(GUObjectArray)中比对该索引下的序列号。
  • 如果序列号不匹配,说明原对象已被销毁,该指针会自动判定为nullptr。这彻底根绝了 C++ 中最恐怖的“野指针”问题。
代码实战:
// 场景:追踪一个可能会被销毁的敌人TWeakObjectPtr<AActor>EnemyWatcher;voidAMyCharacter::Track(AActor*Target){EnemyWatcher=Target;}voidAMyCharacter::Tick(floatDeltaTime){// 必须检查有效性,因为 Target 可能在上一帧被 GC 了if(EnemyWatcher.IsValid()){FVector Loc=EnemyWatcher->GetActorLocation();// 支持重载 -> 操作符}}

🔑 2. TSoftObjectPtr:内存的“节能模式”

TSoftObjectPtr底层存储的是FSoftObjectPath(资产路径字符串)。它让你的 C++ 类与庞大的资产(模型、贴图)之间实现“逻辑关联”而非“内存绑定”。

为什么需要它?

如果你在 C++ 里直接写UPROPERTY() UStaticMesh* Mesh;,那么当你加载这个 C++ 所在的类时,该模型会同步阻塞式加载。如果这种硬引用链条过长,会导致游戏启动或切换地图时出现极长的卡顿。

代码实战:异步加载范式
UPROPERTY(EditAnywhere,Category="Assets")TSoftObjectPtr<UStaticMesh>LazyMesh;voidAMyActor::StartAsyncLoad(){// 1. 检查是否已经在内存中,不在则发起异步请求if(LazyMesh.IsPending()){FStreamableManager&Manager=UAssetManager::GetStreamableManager();Manager.RequestAsyncLoad(LazyMesh.ToSoftObjectPath(),FStreamableDelegate::CreateUObject(this,&AMyActor::OnLoadComplete));}}voidAMyActor::OnLoadComplete(){// 2. 此时 Get() 才会返回真正的指针UStaticMesh*Mesh=LazyMesh.Get();MyComponent->SetStaticMesh(Mesh);}

📊 引用类型深度对比

特性强引用 (UPROPERTY*)弱引用 (TWeakObjectPtr)软引用 (TSoftObjectPtr)
底层存储原始内存地址索引 + 序列号资产路径字符串
对 GC 影响阻止对象被回收不阻止不影响(不加载就不存在)
内存成本高(强制对象驻留内存)极低极低
访问速度极快(直接访问)中(需要一次索引比对)慢(首次访问需加载)
自动置空是(GC 时自动设为 null)是(IsValid 返回 false)否(需手动检查)

🛠️ 实战建议:什么时候该选谁?

  • 父子关系:如果 A 拥有 B(比如角色拥有武器组件),使用强引用
  • 观察关系:如果 A 只是临时关注 B(比如 AI 寻找目标、UI 显示当前选中的对象),使用弱引用
  • 资源配置:对于在编辑器里由策划配置的皮肤、关卡道具、UI 图标,使用软引用
  • 循环引用:如果你发现两个 UObject 互相需要对方的指针,请将其中一方改为弱引用

⚠️ 性能警告

TWeakObjectPtr虽然安全,但它的.Get()操作比原生指针稍慢(存在查表开销)。请避免在Tick函数中对数千个弱引用执行Get()操作,如有必要,请在循环外缓存为本地原生指针。


下一篇预告:《12. UObject 生命周期重载:PostInit, PostLoad, BeginDestroy》

http://www.cnnetsun.cn/news/616148.html

相关文章:

  • 基于改进鲸鱼优化算法的微网系统能量优化管理Matlab源码
  • 【正点原子STM32MP157学习篇】A7和M4联合调试(通过STM32CubeIDE)
  • HoRain云--跨域Cookie设置全攻略
  • 大数据集成性能测试:JMeter压测ETL任务,找出性能瓶颈
  • MyBatis处理模糊查询
  • 如何通过数据分析实现市场细分策略
  • 【无人机任务分配】三维山地地形生成+随机任务点采样+K-means 任务聚类 +任务点排序的无人机航迹规划附Matlab代码
  • 巴西木培养养护的原则
  • 进阶-存储对象4-触发器
  • Spark与Apache Solr集成:高性能搜索方案
  • 2026年免费视频素材网站怎么选?权威推荐榜单top5 自媒体/影视创作/短视频
  • 手把手教你用8款免费AI神器,从零到一轻松搞定毕业论文
  • 大数据实战:如何构建高效的大数据处理平台?
  • 2025_NIPS_Follow-the-Perturbed-Leader Nearly Achieves Best-of-Both-Worlds for the m-Set Semi-Bandit
  • 从零开始学LuatOS:深入理解系统消息列表机制!
  • 18. CPU给GPU传输数据
  • 3.5 静态批处理
  • Marktech推出转模成型封装
  • many sum【牛客tracker 每日一题】
  • 通信原理篇---频率选择性衰落:最大时延差,相关带宽
  • 基于MATLAB的数字滤波器设计及其语音信号去噪应用。 (供学习交流) 其中数字滤波器包括II...
  • 基于SpringBoot的高校实习管理系统毕设源码
  • 深度测评10个AI论文软件,MBA高效写作必备!
  • 完全电子化!一款完全免费的开源电子合同签订系统推荐
  • 架构设计 5 大核心要素
  • TimesFM:Meta推出的下一代时序预测大模型技术解析
  • python基于flask框架的旅社旅游团报名管理系统的设计与实现
  • python基于flask框架的校园兼职系统的设计与实现
  • DeepSeek Engram:给大模型新增一条“记忆稀疏”轴
  • 通过并联电阻改变接收线圈的增益