# Unreal - 智能指针


### ***智能指针***

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

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

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

| *指针类型*            | *何时使用？(最佳实践)*                                       |
| :-------------------- | :----------------------------------------------------------- |
| *`TObjectPtr<T>`*     | *【首选/默认】 任何时候你需要一个指向UObject的指针，都应该优先使用它。无论是成员变量还是局部变量。*<br><br> *核心优势： 当它指向的UObject被销毁时，`TObjectPtr`会自动变为空指针 (`nullptr`)，彻底杜绝了悬挂指针崩溃的风险。* |
| *`UPROPERTY()` 宏*    | *当你需要防止一个UObject被GC回收时，必须用`UPROPERTY()`来标记持有它的指针。 <br>*<br> *黄金组合： `UPROPERTY() TObjectPtr<T>`。`UPROPERTY`负责“保活”，`TObjectPtr`负责“安全访问”。* |
| *`TWeakObjectPtr<T>`* | *当你需要引用一个UObject，但不想阻止它被GC回收时使用。* <br><br> *核心用途： 避免循环引用。例如，一个子Actor想引用它的父Actor，但不希望因为这个引用导致父Actor永远无法被销毁。* |
| *`TSubclassOf<T>`*    | *【极其常用】 当你需要一个变量来持有某个蓝图或C++类本身，以便后续用它来生成(Spawn)新的实例时。 <br/><br/>* *核心优势： 类型安全。它在编辑器和编译期就能保证你赋给这个变量的，必须是指定的基类 T 或其子类。* |
| *`TSoftObjectPtr<T>`* | *【软引用-实例】 当你需要引用一个资源（如纹理、网格体、音效），但不想在游戏一开始就把它加载到内存里时使用<br/><br/>核心优势： 延迟加载 (Lazy Load)。它只存一个路径字符串，不占实际资源内存。你需要时才手动加载（同步或异步），是优化内存和启动速度的神器。* |
| *`TSoftClassPtr<T>`*  | *【软引用-类型】 当你需要引用一个蓝图类（以便后续生成），但不想一开始就把这个蓝图类加载进内存时。<br/><br/>核心优势： 它是 TSubclassOf 的“轻量版”。比如你有100种武器，用硬引用(TSubclassOf)会导致100个武器资源全部加载；用软引用(TSoftClassPtr)则只加载路径，玩家选中哪个才加载哪个。* |

---

#### ***阵营二：管理非 `UObject` 的普通 C++ 类***

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

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

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

| 指针类型                              | 所有权模型                         | 何时使用？                                                   |
| :------------------------------------ | :--------------------------------- | :----------------------------------------------------------- |
| **`TSharedPtr<T>` / `TSharedRef<T>`** | *共享所有权 (Shared Ownership)*    | *【最常用】 当一个对象需要被多个其他对象共同拥有和管理时。它使用引用计数，当最后一个所有者消失时，对象被自动销毁。`TSharedRef`是保证永不为空的版本。* <br><br> *核心用途：Slate UI, 异步任务, 共享的数据管理器。* |
| **`TWeakPtr<T>`**                     | *无所有权 (Non-owning Observer)*   | *当你需要“观察”一个由`TSharedPtr`管理的对象，但不想成为它的所有者（即不想增加引用计数）时。* <br> *核心用途： 缓存指针，以及打破`TSharedPtr`之间的循环引用。* |
| **`TUniquePtr<T>`**                   | *独占所有权 (Exclusive Ownership)* | *当一个对象在任何时候都只能有一个唯一的所有者时。它非常轻量，性能接近原始指针，但不能被复制，只能被移动 (Move)。* <br><br> *核心用途：工厂函数的返回值，Pimpl惯用法。* |

### ***最终决策流程图***

*当你需要一个指针时，这样问自己：*

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

