news 2026/6/15 21:17:32

《Unreal 对 C++ 做了什么》系列 08. 容器的进化:TArray, TMap, TSet 与性能陷阱

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《Unreal 对 C++ 做了什么》系列 08. 容器的进化:TArray, TMap, TSet 与性能陷阱

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

08. 容器的进化:TArray, TMap, TSet 与性能陷阱 📦

🚀 导言:为什么不用 std::vector?

很多刚从标准 C++ 转到 UE 的开发者会问:“既然已经有了成熟的 STL,为什么 UE 还要自己写一套容器?”

答案主要有三点:

  1. 反射集成:标准 STL 容器无法被UPROPERTY识别,引擎无法知道容器里装了什么,也就无法进行自动序列化和 GC 追踪。
  2. 内存控制:UE 需要精准控制内存分配(通过FMemory),以便在不同游戏平台上进行内存对齐和池化管理。
  3. 二进制兼容性:STL 的实现在不同编译器(MSVC vs Clang)之间存在差异,而 UE 的容器在所有平台上行为完全统一。

🔑 TArray:虚幻中最勤劳的“打工人”

TArray是 UE 中最常用的容器,对应std::vector。它将元素存储在一段连续的内存中。

1. 内存增长策略(Slack)

当你向TArray添加元素时,它并不总是只申请刚好足够的内存。

  • Slack(余量):为了避免频繁重新分配内存,TArray会预留一些空闲空间。
  • 性能技巧:如果你预知要装 1000 个元素,请务必使用Reserve(1000)。这能将 N 次内存重分配减少为 1 次。
2. 移除元素的陷阱

在连续内存中删除中间元素,会导致后面的元素全部前移,时间复杂度为 。

  • 优化方案:如果你不介意元素的顺序,请使用RemoveAtSwap(Index)。它会将目标元素与数组末尾元素交换,然后直接删除末尾,时间复杂度降为 。

🔑 TMap 与 TSet:高效的哈希世界

  • TSet:对应std::unordered_set。用于快速查找、去重。
  • TMap:对应std::unordered_map。存储键值对(Key-Value)。

UE 的独特之处:
与 STL 的std::map(通常是红黑树)不同,UE 的TMap默认是**基于哈希(Hash)**的。

  • 稀疏数组架构:UE 的哈希容器底层使用的是“哈希索引 + 稀疏数组”。这意味着即使你删除了中间的元素,容器也不会立刻压缩内存,而是留下“空洞(Hole)”,以保证其他元素的内存地址相对稳定。

🔗 容器与反射系统的“化学反应”

这是 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 系统将看不见这些引用。

  • 后果:数组里的指针所指向的对象会被 GC 回收掉,你的数组里会剩下一堆指向非法内存的野指针。一旦访问,直接 Crash。

📊 核心对比:UE 容器 vs. STL

特性UE 容器 (TArray/TMap)标准 C++ (std::vector/map)
内存分配器使用FMemory(可预测、平台优化)使用默认std::allocator
反射支持支持(加 UPROPERTY 即可)不支持
蓝图接口完美支持不支持
字符串集成FString/FName深度集成需手动转换
迭代器安全性相对较弱(删除操作易导致失效)较强

⚠️ 性能避坑指南

  1. 频繁 Add 的代价:在循环里不断Add
  • 修正:先Reserve足够空间。
  1. 大对象的传参:直接传递整个TArray会触发深拷贝。
  • 修正:始终使用常量引用传递:const TArray<int32>& MyArray
  1. TMap 的 Key 选择
  • 如果你用FString做 Key,性能尚可。
  • 如果你追求极致性能,请改用FName。因为FName本质上是一个整数 ID,哈希计算速度极快。

结语

UE 的容器不仅仅是数据的仓库,它们是反射系统的一部分。TArray 是你的首选,因为它对 CPU 缓存最友好;只有在需要高速查找时才考虑TMap/TSet。记住:永远给存储 UObject 指针的容器加上UPROPERTY(),这是保命的准则。


下一篇我们将探讨:《09. 字符串的“三重奏”:FName, FText, FString 的分工与转换》。我们将看看为什么 UE 这么麻烦,非要设计三种不同的字符串类型。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 10:30:32

告别信息刷屏!RssHub+cpolar 让你的订阅源随时随地能看

RssHub 的核心功能是给各种网站生成 RSS 订阅源&#xff0c;不管是微博热搜、GitHub 仓库更新&#xff0c;还是豆瓣小组、慕课课程&#xff0c;它都能把内容整理成标准格式&#xff0c;方便用阅读器统一查看。适合需要实时追踪信息的自媒体人、关注代码动态的程序员&#xff0c…

作者头像 李华
网站建设 2026/6/15 14:58:55

认识定性数据和定量数据和描述性统计的常规分析

下面内容摘录自《用R探索医药数据科学》专栏文章的部分内容&#xff08;原文5342字&#xff09;。 2篇3章1节&#xff1a;定量数据的集中趋势描述&#xff0c;文末有众数的自定义函数-CSDN博客 在科研中&#xff0c;很多资料经过整理之后&#xff0c;常常需要进行一系列的统计…

作者头像 李华
网站建设 2026/6/14 10:53:14

【Docker安全监控终极指南】:揭秘Falco实时监控的5大核心应用场景

第一章&#xff1a;Docker安全监控的演进与Falco的崛起 随着容器化技术的广泛应用&#xff0c;Docker已成为现代应用部署的核心组件。然而&#xff0c;其轻量、动态和短暂的特性也带来了新的安全挑战。传统的主机级安全监控工具难以深入容器内部行为&#xff0c;无法有效检测异…

作者头像 李华
网站建设 2026/6/15 14:54:39

系统提示词怎么写?教你设置‘你是一个编程助手’提升准确率

系统提示词怎么写&#xff1f;教你设置“你是一个编程助手”提升准确率 在算法竞赛训练营里&#xff0c;一位学生正盯着屏幕发愁&#xff1a;他把一道经典的动态规划题输入到本地部署的小模型中&#xff0c;结果返回的答案不仅逻辑混乱&#xff0c;甚至连基本的边界条件都没考虑…

作者头像 李华
网站建设 2026/6/15 16:01:38

计算机毕业设计springboot基于BS模式的图书馆管理系统 基于SpringBoot与Vue的浏览器端图书馆综合服务平台 B/S架构下融合SpringBoot的校园数字图书馆运营系统

计算机毕业设计springboot基于BS模式的图书馆管理系统45j5x81v &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。互联网把纸质藏书从“书架”搬向“云端”&#xff0c;也让“借还”…

作者头像 李华
网站建设 2026/6/15 14:58:30

计算机毕业设计springboot环保监督管理系统 基于SpringBoot的生态环境智能监管平台 绿色守护:SpringBoot驱动的环保监测与执法一体化系统

计算机毕业设计springboot环保监督管理系统2y7zsjty &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 工业化与城市化的高速推进让空气、水体、噪声等污染事件频发&#xff0c;传…

作者头像 李华