码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • UE4 C++:TArray容器


    目录

    概述

    创建

    添加元素

    Init :填充多个相同值

    Add:可添加重复元素,添加时会创建临时变量再复制

    AddUnique:不可添加重复元素

    Emplace:添加时不会创建临时变量,性能好于Add

    Append: 可一次性添加其他TArray中的多个元素,或者指向常规C数组的指针及该数组的大小

    Insert:在给定索引处添加单个元素或元素数组的副本

    SetNum: 函数可直接设置数组元素的数量。

    迭代

    基于范围的for语句

    常规for

    迭代器

    排序

    Sort

    HeapSort:堆排序(不稳定)

    StableSort:排序后保证等值元素的相对排序。StableSort 作为归并排序实现

    查询

    GetData:函数返回指向数组中元素的指针,该操作直接访问数组内存

    GetTypeSize:获取单个元素的大小

    []:索引运算符获取元素,返回的是一个引用,可用于操作元素

    IsValidIndex:询问容器,特定索引是否有效(0≤index())<>

    Last:从数组末端反向索引,默认为0

    Top:返回最后一个元素,不接受索引

    Contains:是否包含特定元素

    ContainsByPredicate:是否包含与特定谓词匹配的元素

    Find:元素是否存在并返回找到的首个元素索引

    FindLast:元素是否存在并返回找到的最末元素索引

    IndexOfByKey:返回首个匹配到的元素的索引;未找到返回INDEX_NONE

    IndexOfByPredicate:查找与特定谓词匹配的首个元素的索引;

    FindByKey:将元素和任意对象对比,并返回首个匹配到的元素的指针,如果未匹配到,则返回nullptr

    FindByPredicate:和IndexOfByPredicate相似,不同的是,它的返回值是指针,而不是索引

    FilterByPredicate:可获取与特定谓词匹配的元素数组

    移除元素

    Remove:移除数组中的元素

    RemoveSingle:移除数组中首个匹配元素

    RemoveAt:按照索引移除元素

    RemoveAll:移除与谓词匹配的元素

    RemovSwap、RemoveAtSwap、RemoveAllSwap

    Empty:移除数组中所有元素

    Reset:与 Empty类似,该函数将不释放内存

    运算符

    +=:可替代Append函数进行数组连接

    MoveTemp:可将一个数组中的内容移动到另一个数组中,源数组将被清空

    ==、!=:可对数组进行比较

    堆

    Heapify:可将现有数组转换为堆

    HeapPush:新元素添加到堆,对其他节点进行重新排序,以对堆进行维护

    HeapPop、HeadPopDiscard:移除堆顶结点

    HeapRemoveAt:删除数组中给定索引处的元素,然后重新排列元素,对堆进行维护

    HeapTop:查看堆的顶部节点,无需变更数组

    Slack

    GetSlack:找出数组中的Slack量,相当于Max()-Min() 

    Max:获取到容器重新分配之前数组可保存的最大元素数量

    Empty()

    Reset():与Empty函数类似,不同之处是若当前内存分配已提供请求的Slack,该函数将不释放内存。但若请求的Slack较大,其将分配更多内存

    Shrink():移除所有Slack

    原始内存


    概述

    • TArray是UE中最常用的容器类。速度快、内存消耗小、安全性高
    • 其设计时未考虑扩展问题,因此建议在实际操作中 不要使用 new 和 delete 创建和销毁TArray实例
    • TArray元素作为数值类型时,被销毁时其中的元素也将被销毁。若在另以TArray中创建TArray变量,其元素将复制到新变量中,且不会共享状态

    创建

    TArray IntArray;
    

    添加元素

    Init :填充多个相同值

    IntArray.Init(10, 5); //==>[10,10,10,10,10]
    

    Add:可添加重复元素,添加时会创建临时变量再复制

    AddUnique:不可添加重复元素

    Emplace:添加时不会创建临时变量,性能好于Add

    1. TArray StrArr;
    2. StrArr.Add(TEXT("Hello"));
    3. IntArray.AddUnique(TEXT("Hello"));
    4. StrArr.Emplace(TEXT("World")); //==>["Hello","World"]

    Append: 可一次性添加其他TArray中的多个元素,或者指向常规C数组的指针及该数组的大小

    1. FString Arr[] = { TEXT("of"), TEXT("Tomorrow") };
    2. StrArr.Append(Arr, ARRAY_COUNT(Arr)); //==>["Hello","World","of","Tomorrow"]

    新元素添加到数组时,数组的分配器将根据需要分配内存。当前数组大小超出时,默认分配器将添加足够的内存,用于存储多个新元素。Add 和 Emplace 函数的多数效果相同,细微区别在于:

    • Add(或 Push)将元素类型的实例复制(或移动)到数组中。

    • Emplace 使用给定参数构建元素类型的新实例。

    因此在 TArray 中,Add 将用字符串文字创建临时 FString,然后将该临时 FString 的内容移至容器内的新 FString 中;而 Emplace 将用字符串文字直接新建 FString。最终结果相同,但 Emplace 可避免创建临时文件。对于 FString 等非浅显数值类型而言,临时文件通常有害无益。

    总体而言,Emplace 优于 Add,因此其可避免在调用点创建无需临时变量,并将此类变量复制或移动到容器中。根据经验,可将 Add 用于浅显类型,将 Emplace 用于其他类型。Emplace 的效率始终高于 Add,但 Add 的可读性可能更好。

    仅在尚不存在等值元素时,AddUnique 才会向容器添加新元素。使用以下元素类型的运算符检查等值性:运算符==:

    1. StrArr.AddUnique(TEXT("!"));
    2. // StrArr == ["Hello","World","of","Tomorrow","!"]
    3. StrArr.AddUnique(TEXT("!"));
    4. // StrArr is unchanged as "!" is already an element

    Insert:在给定索引处添加单个元素或元素数组的副本

    StrArr.Insert(TEXT("Brave"), 1); //==>["Hello","Brave","World","of","Tomorrow","!"]
    

    SetNum: 函数可直接设置数组元素的数量。

    • 如新数量大于当前数量,则使用元素类型的默认构造函数新建元素
    • 如新数量小于当前数量,SetNum 将移除元素。
    1. StrArr.SetNum(8); //==>["Hello","Brave","World","of","Tomorrow","!","",""]
    2. StrArr.SetNum(6); //==>["Hello","Brave","World","of","Tomorrow","!"]

    迭代

    基于范围的for语句

    1. FString JoinedStr;
    2. for (auto& Str :StrArr)
    3. {
    4. JoinedStr += Str;
    5. JoinedStr += TEXT(" ");
    6. } // JoinedStr == "Hello Brave World of Tomorrow !"

    常规for

    1. for (int32 Index = 0; Index != StrArr.Num(); ++Index)
    2. {
    3. JoinedStr += StrArr[Index];
    4. JoinedStr += TEXT(" ");
    5. }

    迭代器

    函数 CreateIterator 和 CreateConstIterator 可分别用于元素的读写和只读访问

    1. for (auto It = StrArr.CreateConstIterator(); It; ++It)
    2. {
    3. JoinedStr += *It;
    4. JoinedStr += TEXT(" ");
    5. }

     

    排序

    Sort

    StrArr.Sort(); //==>["!","Brave","Hello","of","Tomorrow","World"]
    

    二进制谓词提供不同排序规则(lambda)

    1. StrArr.Sort([](const FString& A, const FString& B){
    2. return A.len() < B.len();
    3. }); //按字符串长度排序, ==>["!","of","Hello","Brave","World","Tomorrow"]

    HeapSort:堆排序(不稳定)

    无论带或不带二元谓词,均可用于执行对排序。是否选择使用它则取决于特定数据和与 Sort 函数之间的排序效率对比。和 Sort 一样,HeapSort 并不稳定

    1. StrArr.HeapSort([](const FString& A, const FString& B) {
    2. return A.Len() < B.Len();
    3. }); //==>["!","of","Hello","Brave","World","Tomorrow"]

    StableSort:排序后保证等值元素的相对排序。StableSort 作为归并排序实现

    1. StrArr.StableSort([](const FString& A, const FString& B) {
    2. return A.Len() < B.Len();
    3. }); //==>["!","of","Brave","Hello","World","Tomorrow"]

    查询

    Num:查询元素数量

    int32 Count = StrArr.Num();  // Count == 6
    

    GetData:函数返回指向数组中元素的指针,该操作直接访问数组内存

    仅在数组存在且未执行更改数组的操作时,此指针方有效。仅 StrPtr 的首个 Num 指数才可被解除引用

    1. FString* StrPtr = StrArr.GetData();
    2. // StrPtr[0] == "!"
    3. // StrPtr[1] == "of"
    4. // ...
    5. // StrPtr[5] == "Tomorrow"
    6. // StrPtr[6] - undefined behavior

    GetTypeSize:获取单个元素的大小

    uint32 ElementSize = StrArr.GetTypeSize(); // ElementSize == sizeof(FString)
    

    []:索引运算符获取元素,返回的是一个引用,可用于操作元素

    1. FString Elem1 = StrArr[1]; // Elem1 == "of"
    2. StrArr[3] = StrArr[3].ToUpper(); //==>["!","of","Brave","HELLO","World","Tomorrow"]

    IsValidIndex:询问容器,特定索引是否有效(0≤index
    1. bool bValidM1 = StrArr.IsValidIndex(-1);// bValidM1 == false
    2. bool bValid0 = StrArr.IsValidIndex(0); // bValid0 == true
    3. bool bValid5 = StrArr.IsValidIndex(5); // bValid5 == true
    4. bool bValid6 = StrArr.IsValidIndex(6); // bValid6 == false

    Last:从数组末端反向索引,默认为0

    Top:返回最后一个元素,不接受索引

    1. FString ElemEnd = StrArr.Last(); // ElemEnd == "Tomorrow"
    2. FString ElemEnd0 = StrArr.Last(0); // ElemEnd0 == "Tomorrow"
    3. FString ElemEnd1 = StrArr.Last(1); // ElemEnd1 == "World"
    4. FString ElemTop = StrArr.Top(); // ElemTop == "Tomorrow"

    Contains:是否包含特定元素

    1. bool bHello = StrArr.Contains(TEXT("Hello")); // bHello == true
    2. bool bGoodbye = StrArr.Contains(TEXT("Goodbye")); // bGoodbye == false

    ContainsByPredicate:是否包含与特定谓词匹配的元素

    1. bool bLen5 = StrArr.ContainsByPredicate([](const FString& Str){
    2. return Str.Len() == 5;
    3. }); // bLen5 == true
    4. bool bLen6 = StrArr.ContainsByPredicate([](const FString& Str){
    5. return Str.Len() == 6;
    6. }); // bLen6 == false

    Find:元素是否存在并返回找到的首个元素索引

    1. int32 Index;
    2. if (StrArr.Find(TEXT("Hello"), Index)){
    3. // Index == 3
    4. }
    5. int32 Index2 = StrArr.Find(TEXT("Hello")); // Index2 == 3
    6. int32 IndexNone = StrArr.Find(TEXT("None")); // IndexNone == INDEX_NONE(实质上是-1)

    FindLast:元素是否存在并返回找到的最末元素索引

    1. int32 IndexLast;
    2. if (StrArr.FindLast(TEXT("Hello"), IndexLast)){
    3. // IndexLast == 3, because there aren't any duplicates
    4. }
    5. int32 IndexLast2 = StrArr.FindLast(TEXT("Hello")); // IndexLast2 == 3

    IndexOfByKey:返回首个匹配到的元素的索引;未找到返回INDEX_NONE

    IndexOfByPredicate:查找与特定谓词匹配的首个元素的索引;

    1. int32 Index = StrArr.IndexOfByPredicate([](const FString& Str){
    2. return Str.Contains(TEXT("r"));
    3. }); // Index == 2

    FindByKey:将元素和任意对象对比,并返回首个匹配到的元素的指针,如果未匹配到,则返回nullptr

    1. auto* OfPtr = StrArr.FindByKey(TEXT("of"))); // OfPtr == &StrArr[1]
    2. auto* ThePtr = StrArr.FindByKey(TEXT("the"))); // ThePtr == nullptr

    FindByPredicate:和IndexOfByPredicate相似,不同的是,它的返回值是指针,而不是索引

    1. auto* Len5Ptr = StrArr.FindByPredicate([](const FString& Str){
    2. return Str.Len() == 5;
    3. }); // Len5Ptr == &StrArr[2]
    4. auto* Len6Ptr = StrArr.FindByPredicate([](const FString& Str){
    5. return Str.Len() == 6;
    6. }); // Len6Ptr == nullptr

    FilterByPredicate:可获取与特定谓词匹配的元素数组

    1. auto Filter = StrArray.FilterByPredicate([](const FString& Str){
    2. return !Str.IsEmpty() && Str[0] < TEXT('M');
    3. });

    移除元素

    Remove:移除数组中的元素

    1. TArray ValArr;
    2. int32 Temp[] = { 10, 20, 30, 5, 10, 15, 20, 25, 30 };
    3. ValArr.Append(Temp, ARRAY_COUNT(Temp)); //==>[10,20,30,5,10,15,20,25,30]
    4. ValArr.Remove(20); //==>[10,30,5,10,15,25,30]

    RemoveSingle:移除数组中首个匹配元素

    ValArr.RemoveSingle(30); //==>[10,5,10,15,25,30]
    

    RemoveAt:按照索引移除元素

    1. ValArr.RemoveAt(2); // 移除下标为2的元素, ==>[10,5,15,25,30]
    2. ValArr.RemoveAt(99); // 引发错误,越界

    RemoveAll:移除与谓词匹配的元素

    1. ValArr.RemoveAll([](int32 Val) {
    2. return Val % 3 == 0; }); //移除为3倍数的所有数值, ==> [10,5,25]

    RemovSwap、RemoveAtSwap、RemoveAllSwap

    移动过程存在开销。如不需要剩余元素排序,可使用 RemoveSwap、RemoveAtSwap 和 RemoveAllSwap 函数减少此开销。此类函数的工作方式与其非交换变种相似,不同之处在于其不保证剩余元素的排序,因此可更快地完成任务:

    1. TArray ValArr2;
    2. for (int32 i = 0; i != 10; ++i)
    3. ValArr2.Add(i % 5); //==>[0,1,2,3,4,0,1,2,3,4]
    4. ValArr2.RemoveSwap(2); //==>[0,1,4,3,4,0,1,3]
    5. ValArr2.RemoveAtSwap(1); //==>[0,3,4,3,4,0,1]
    6. ValArr2.RemoveAllSwap([](int32 Val) {
    7. return Val % 3 == 0; }); //==>[1,4,4]

    Empty:移除数组中所有元素

    ValArr2.Empty(); //==>[]
    

    Reset:与 Empty类似,该函数将不释放内存

    ValArr2.Reset (); //==>[]
    

    运算符

    数组是常规数值类型,可使用标准复制构造函数或赋值运算符进行复制。由于数组严格拥有其元素,复制数组的操作是深层的,因此新数组将拥有其自身的元素副本

    1. TArray ValArr3;
    2. ValArr3.Add(1);
    3. ValArr3.Add(2);
    4. ValArr3.Add(3);
    5. auto ValArr4 = ValArr3; // ValArr4 == [1,2,3];
    6. ValArr4[0] = 5; // ValArr4 == [5,2,3]; ValArr3 == [1,2,3];

    +=:可替代Append函数进行数组连接

    ValArr4 += ValArr3; //==>[5,2,3,1,2,3]
    

    MoveTemp:可将一个数组中的内容移动到另一个数组中,源数组将被清空

    ValArr3 = MoveTemp(ValArr4);  // ValArr3 == [5,2,3,1,2,3]; ValArr4 == []
    

    ==、!=:可对数组进行比较

    元素的排序很重要:只有元素的顺序和数量相同时,两个数组才被视为相同

    1. TArray FlavorArr1;
    2. FlavorArr1.Emplace(TEXT("Chocolate"));
    3. FlavorArr1.Emplace(TEXT("Vanilla")); // FlavorArr1 == ["Chocolate","Vanilla"]
    4. auto FlavorArr2 = FlavorArr1; // FlavorArr2 == ["Chocolate","Vanilla"]
    5. bool bComparison1 = FlavorArr1 == FlavorArr2; // bComparison1 == true
    6. for ( auto& Str : FlavorArr2 )
    7. {
    8. Str = Str.ToUpper();
    9. } // FlavorArr2 == ["CHOCOLATE","VANILLA"]
    10. bool bComparison2 = FlavorArr1 == FlavorArr2; // bComparison2 == true,因为FString的对比忽略大小写
    11. Exchange(FlavorArr2[0], FlavorArr2[1]); // FlavorArr2 == ["VANILLA","CHOCOLATE"]
    12. bool bComparison3 = FlavorArr1 == FlavorArr2; // bComparison3 == false,因为两个数组内的元素顺序不同

    堆

    TArray 拥有支持二叉堆数据结构的函数。堆是一种二叉树,其中父节点的排序等于或高于其子节点。作为数组实现时,树的根节点位于元素0,索引N处节点的左右子节点的指数分别为2N+1和2N+2。子节点彼此间不存在特定排序。

    Heapify:可将现有数组转换为堆

    1. TArray HeapArr;
    2. for (int32 Val = 10; Val != 0; --Val){
    3. HeapArr.Add(Val);
    4. } // HeapArr == [10,9,8,7,6,5,4,3,2,1]
    5. HeapArr.Heapify(); // HeapArr == [1,2,4,3,6,5,8,10,7,9]

    HeapPush:新元素添加到堆,对其他节点进行重新排序,以对堆进行维护

    HeapArr.HeapPush(4); // HeapArr == [1,2,4,3,4,5,8,10,7,9,6]
    

    HeapPop、HeadPopDiscard:移除堆顶结点

    这两个函数的区别在于前者引用元素的类型来返回顶部元素的副本,而后者只是简单地移除顶部节点,不进行任何形式的返回。两个函数得出的数组变更一致,重新正确排序其他元素可对堆进行维护

    1. int32 TopNode;
    2. HeapArr.HeapPop(TopNode); // TopNode == 1; HeapArr == [2,3,4,6,4,5,8,10,7,9]

    HeapRemoveAt:删除数组中给定索引处的元素,然后重新排列元素,对堆进行维护

    HeapArr.HeapRemoveAt(1); // HeapArr == [2,4,4,6,9,5,8,10,7]
    

    HeapTop:查看堆的顶部节点,无需变更数组

    int32 Top = HeapArr.HeapTop();  // Top == 2
    

    Slack

    因为数组的尺寸可进行调整,因此它们使用的是可变内存量。为避免每次添加元素时需要重新分配,分配器通常会提供比需求更多的内存,使之后进行的Add调用不会因为重新分配而出现性能损失。同样,删除元素通常不会释放内存。

    • 容器中现有元素数量和下次分配之前可添加的元素数量之差为Slack
    • 默认构建的数组不分配内存,slack初始为0

    GetSlack:找出数组中的Slack量,相当于Max()-Min() 

    Max:获取到容器重新分配之前数组可保存的最大元素数量

    • 分配器确定重新分配后的容器中的Slack量,因此Slack不是常量
    1. TArray SlackArray;
    2. // SlackArray.GetSlack() == 0
    3. // SlackArray.Num() == 0
    4. // SlackArray.Max() == 0
    5. SlackArray.Add(1);
    6. // SlackArray.GetSlack() == 3
    7. // SlackArray.Num() == 1
    8. // SlackArray.Max() == 4
    9. SlackArray.Add(2);
    10. SlackArray.Add(3);
    11. SlackArray.Add(4);
    12. SlackArray.Add(5);
    13. // SlackArray.GetSlack() == 17
    14. // SlackArray.Num() == 5
    15. // SlackArray.Max() == 22
    • 虽然无需管理Slack,但可管理Slack对数组优化,以满足需求

    Empty()

    例如,如需要向数组添加大约100个新元素,则可在添加前确保拥有可至少存储100个新元素的Slack,以便添加新元素时无需分配内存。上文所述的 Empty 函数接受可选Slack参数

    1. SlackArray.Empty();
    2. // SlackArray.GetSlack() == 0
    3. // SlackArray.Num() == 0
    4. // SlackArray.Max() == 0
    5. SlackArray.Empty(3);
    6. // SlackArray.GetSlack() == 3
    7. // SlackArray.Num() == 0
    8. // SlackArray.Max() == 3
    9. SlackArray.Add(1);
    10. SlackArray.Add(2);
    11. SlackArray.Add(3);
    12. // SlackArray.GetSlack() == 0
    13. // SlackArray.Num() == 3
    14. // SlackArray.Max() == 3

    Reset():与Empty函数类似,不同之处是若当前内存分配已提供请求的Slack,该函数将不释放内存。但若请求的Slack较大,其将分配更多内存

    1. SlackArray.Reset(0);
    2. // SlackArray.GetSlack() == 3
    3. // SlackArray.Num() == 0
    4. // SlackArray.Max() == 3
    5. SlackArray.Reset(10);
    6. // SlackArray.GetSlack() == 10
    7. // SlackArray.Num() == 0
    8. // SlackArray.Max() == 10

    Shrink():移除所有Slack

    此函数将把分配重新调整为所需要的大小,使其保存当前的元素序列,而无需实际移动元素

    1. SlackArray.Add(5);
    2. SlackArray.Add(10);
    3. SlackArray.Add(15);
    4. SlackArray.Add(20);
    5. // SlackArray.GetSlack() == 6
    6. // SlackArray.Num() == 4
    7. // SlackArray.Max() == 10
    8. SlackArray.Shrink();
    9. // SlackArray.GetSlack() == 0
    10. // SlackArray.Num() == 4
    11. // SlackArray.Max() == 4

    原始内存

    参考链接:

     【UE4 C++ 基础知识】<5> 容器——TArray - 砥才人 - 博客园 (cnblogs.com)

    Array Containers in Unreal Engine | 虚幻引擎5.0文档

  • 相关阅读:
    JSON(JavaScript Object Notation)
    【已解决】immer_project immer 动态确定对象属性修改的控制不恰当
    Java JSON组成和解析
    详解IPD需求分析工具$APPEALS
    【indexedDB】indexedDB知识梳理
    (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
    低代码平台AWS PaaS_安装应用商店的标准应用(安装单一应用)
    JavaScript数据结构与算法-排序全详解
    AI视频监控平台EasyCVR接入海康SDK出现异常,该如何解决?
    OpenCV计算机视觉学习(14)——浅谈常见图像后缀(png, jpg, bmp)的区别(opencv读取语义分割mask的坑)
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126504319
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号