游戏开发中必须用tuniqueptr而非裸指针,因其强制转移语义可杜绝内存泄漏,适用于关卡流送、异步加载等“一次性重资产”场景,且activebuild.reset()是明确释放信号而非可选操作。

游戏开发里用指针不是为了炫技,而是解决几个硬性问题:对象生命周期必须可控、内存不能抖动、跨线程访问要安全、循环引用得能破。UE 的 TUniquePtr、TSharedPtr、TWeakPtr 就是按这些场景设计的,不是随便套个智能指针就完事。
什么时候必须用 TUniquePtr 而不是裸指针或 new
裸指针 + delete 在多人协作或异常路径下极易漏释放;new 出来的东西若没配对 delete,就是稳定泄漏。而 TUniquePtr 强制转移语义,天然适合“一次性重资产”场景:
- 关卡流送中临时构建的几何上下文(几百 KB~MB 级原始顶点/索引数据),构建完立刻交给引擎接管,自己不再持有
- 异步加载任务中生成的中间解析结果,只在当前任务内有效,提交后直接 move 出去,避免拷贝和共享竞争
- 某些系统初始化阶段的配置解析器,用完即焚,不允许被其他模块意外延长寿命
示例中 ActiveBuild.Reset() 不是可选操作——它是明确释放时机的信号,不是靠析构延迟。
TSharedPtr 和 TSharedRef 的关键区别在哪
区别就在“是否允许为空”。TSharedPtr 可以是空的,适合读取型共享;TSharedRef 一旦创建就永不为空,适合必须有效的运行时句柄:
-
TSharedPtr<fcraftingrecipetable></fcraftingrecipetable>:全局配方表,可能还没加载完,UI 拿到后得先判空再查Recipes.Contains(Id) -
TSharedRef<swidget></swidget>或TSharedRef<fgameplayability></fgameplayability>:UI 组件或能力实例,一旦构造完成就必须可用,传参/存储时不需额外空检查 - 误把
TSharedRef当TSharedPtr用会导致编译失败(构造函数不接受 nullptr),这是有意为之的安全边界
为什么 TWeakPtr 是破循环引用的唯一可靠手段
UE 中常见模式:A 持有 TSharedPtr<b></b>,B 又回调 A 的某个方法——如果 B 也用 TSharedPtr<a></a>,就锁死释放。这时只能让 B 持有 TWeakPtr<a></a>:
-
TWeakPtr不增加引用计数,只是“观察”目标对象是否还活着 - 使用前必须调用
Pin()得到临时TSharedPtr,失败说明目标已销毁 - 常用于事件订阅(如 UI 响应 Actor 状态变化)、缓存句柄(如技能冷却倒计时引用技能对象)、异步回调(线程池任务完成后回主线程更新 UI)
- 注意:
TWeakPtr本身不保活,Pin()失败后不能 fallback 到默认值——得按业务逻辑处理“对象已消失”这个事实
真正难的不是记住语法,而是判断“谁该拥有”“谁该观察”“谁该转移”。比如一个技能结算上下文,若只在单线程内跑完,栈对象或 TUniquePtr 最优;一旦涉及线程池+回调,就得在提交时 decide 是 move 还是 share——这个决策点,没人能替你做。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











