目录

Unreal - 智能指针

智能指针

  • 问题背景: 我们之前提到,在堆(Heap)上用 new 创建了对象,就必须在某个地方用 delete 来销毁它。这个过程完全靠程序员手动管理,极其容易出错。忘记 delete 会导致内存泄漏;对同一个指针 delete 两次会导致程序崩溃;访问一个已经被 delete 的指针(悬挂指针)也是灾难性的。
  • 解决方案: 智能指针!它是一个C++对象(一个“管家”),内部包装了一个原始指针(一把“钥匙”)。这个管家对象利用C++的作用域规则,在自己被销毁时(例如函数结束、对象生命周期结束),自动地、确定地 delete 它所管理的指针。

管理 UObject 及其派生类 (如 AActor, UActorComponent)

UObject 是Unreal引擎的核心,所有继承自它的对象都由垃圾回收 (Garbage Collection, GC) 系统统一管理生命周期。我们要做的是正确地“配合”GC工作。

指针类型 何时使用?(最佳实践)
TObjectPtr<T> 【首选/默认】 任何时候你需要一个指向UObject的指针,都应该优先使用它。无论是成员变量还是局部变量。

核心优势: 当它指向的UObject被销毁时,TObjectPtr会自动变为空指针 (nullptr),彻底杜绝了悬挂指针崩溃的风险。
UPROPERTY() 当你需要防止一个UObject被GC回收时,必须用UPROPERTY()来标记持有它的指针。

黄金组合: UPROPERTY() TObjectPtr<T>UPROPERTY负责“保活”,TObjectPtr负责“安全访问”。
TWeakObjectPtr<T> 当你需要引用一个UObject,但不想阻止它被GC回收时使用。

核心用途: 避免循环引用。例如,一个子Actor想引用它的父Actor,但不希望因为这个引用导致父Actor永远无法被销毁。
TSubclassOf<T> 【极其常用】 当你需要一个变量来持有某个蓝图或C++类本身,以便后续用它来生成(Spawn)新的实例时。

核心优势: 类型安全。它在编辑器和编译期就能保证你赋给这个变量的,必须是指定的基类 T 或其子类。
TSoftObjectPtr<T> 【软引用-实例】 当你需要引用一个资源(如纹理、网格体、音效),但不想在游戏一开始就把它加载到内存里时使用

核心优势: 延迟加载 (Lazy Load)。它只存一个路径字符串,不占实际资源内存。你需要时才手动加载(同步或异步),是优化内存和启动速度的神器。
TSoftClassPtr<T> 【软引用-类型】 当你需要引用一个蓝图类(以便后续生成),但不想一开始就把这个蓝图类加载进内存时。

核心优势: 它是 TSubclassOf 的“轻量版”。比如你有100种武器,用硬引用(TSubclassOf)会导致100个武器资源全部加载;用软引用(TSoftClassPtr)则只加载路径,玩家选中哪个才加载哪个。

阵营二:管理非 UObject 的普通 C++ 类

对于那些你自己创建的、不继承自 UOject 的普通C++类(例如 Slate UI 的逻辑类、自定义的数据管理器等),GC系统完全不管它们。

这时,我们就需要请出C++的传统智能指针来手动管理它们的生命周期。

说是c++的传统指针,其实也是Unreal在于基础上自己封装的

指针类型 所有权模型 何时使用?
TSharedPtr<T> / TSharedRef<T> 共享所有权 (Shared Ownership) 【最常用】 当一个对象需要被多个其他对象共同拥有和管理时。它使用引用计数,当最后一个所有者消失时,对象被自动销毁。TSharedRef是保证永不为空的版本。

核心用途:Slate UI, 异步任务, 共享的数据管理器。
TWeakPtr<T> 无所有权 (Non-owning Observer) 当你需要“观察”一个由TSharedPtr管理的对象,但不想成为它的所有者(即不想增加引用计数)时。
核心用途: 缓存指针,以及打破TSharedPtr之间的循环引用。
TUniquePtr<T> 独占所有权 (Exclusive Ownership) 当一个对象在任何时候都只能有一个唯一的所有者时。它非常轻量,性能接近原始指针,但不能被复制,只能被移动 (Move)。

核心用途:工厂函数的返回值,Pimpl惯用法。

最终决策流程图

当你需要一个指针时,这样问自己:

  1. 我要指向的对象是 UObject 吗? (Actor, Widget, Asset等)
    • 否 (普通 C++ 类/结构体)
      • 我需要独占它吗? (生命周期随我结束)
        • 是 -> TUniquePtr (最高效)
      • 我需要和其他人共享它吗?
        • 是 -> TSharedPtr (配合 TWeakPtr 破除循环)
    • 是 (UObject 体系)
      • 它必须现在就在内存里吗? (还是说只是个硬盘路径?)
        • 否 (硬盘路径/延迟加载)
          • 指向具体资源 (如纹理) -> TSoftObjectPtr
          • 指向蓝图类 (如 BP_Enemy) -> TSoftClassPtr
        • 是 (内存中存在的对象)
          • 它是“类”还是“实例”?
            • 是类 (用于生成) -> TSubclassOf
            • 是实例 (活着的对象)
              • 我负责“保活”它吗? (我是它的主人?)
                • 是 -> UPROPERTY() + TObjectPtr (强引用)
                • 否 (我只是引用/观察,它可能死掉)
                  • 是 -> TWeakObjectPtr (安全,死后变nullptr)