开放式代码审查实践指南:告别走过场,让Code Review真正生效
2026/9/25 5:49:08
很多刚从标准 C++ 转到 UE 的开发者会问:“既然已经有了成熟的 STL,为什么 UE 还要自己写一套容器?”
答案主要有三点:
UPROPERTY识别,引擎无法知道容器里装了什么,也就无法进行自动序列化和 GC 追踪。FMemory),以便在不同游戏平台上进行内存对齐和池化管理。TArray是 UE 中最常用的容器,对应std::vector。它将元素存储在一段连续的内存中。
当你向TArray添加元素时,它并不总是只申请刚好足够的内存。
TArray会预留一些空闲空间。Reserve(1000)。这能将 N 次内存重分配减少为 1 次。在连续内存中删除中间元素,会导致后面的元素全部前移,时间复杂度为 。
RemoveAtSwap(Index)。它会将目标元素与数组末尾元素交换,然后直接删除末尾,时间复杂度降为 。std::unordered_set。用于快速查找、去重。std::unordered_map。存储键值对(Key-Value)。UE 的独特之处:
与 STL 的std::map(通常是红黑树)不同,UE 的TMap默认是**基于哈希(Hash)**的。
这是 UE 对容器做的最核心改造:让容器感知对象。
UCLASS()classAMyInventory:publicAActor{GENERATED_BODY()// 1. 自动序列化:引擎会自动把数组里的数据存入 .uasset// 2. GC 追踪:如果数组存的是 UObject*,GC 会确保它们不被误杀UPROPERTY(EditAnywhere,BlueprintReadWrite)TArray<UItem*>Items;// 3. 蓝图暴露:蓝图可以直接操作这个 TMapUPROPERTY(EditAnywhere)TMap<FString,int32>ItemScores;};极其重要的警告:
如果你的TArray存储的是UObject*(比如TArray<AActor*>),但你没有加UPROPERTY(),那么 GC 系统将看不见这些引用。
| 特性 | UE 容器 (TArray/TMap) | 标准 C++ (std::vector/map) |
|---|---|---|
| 内存分配器 | 使用FMemory(可预测、平台优化) | 使用默认std::allocator |
| 反射支持 | 支持(加 UPROPERTY 即可) | 不支持 |
| 蓝图接口 | 完美支持 | 不支持 |
| 字符串集成 | 与FString/FName深度集成 | 需手动转换 |
| 迭代器安全性 | 相对较弱(删除操作易导致失效) | 较强 |
Add。Reserve足够空间。TArray会触发深拷贝。const TArray<int32>& MyArray。FString做 Key,性能尚可。FName。因为FName本质上是一个整数 ID,哈希计算速度极快。UE 的容器不仅仅是数据的仓库,它们是反射系统的一部分。TArray 是你的首选,因为它对 CPU 缓存最友好;只有在需要高速查找时才考虑TMap/TSet。记住:永远给存储 UObject 指针的容器加上UPROPERTY(),这是保命的准则。
下一篇我们将探讨:《09. 字符串的“三重奏”:FName, FText, FString 的分工与转换》。我们将看看为什么 UE 这么麻烦,非要设计三种不同的字符串类型。