【UE4】TMap的使用

老子叫甜甜 提交于 2019-12-13 16:41:10

TMap

在 TArray 之后,虚幻引擎 4(UE4)中最常用的容器是 TMap。此容器是一个关联式容器,意味着每个键均拥有一个关联值,可通过键高效地查找值对象。

存在两种映射类型:TMap 和 TMultiMap。TMap 的键为唯一。键已存在时插入一个新的键值对将导致现有的对被替代。TMultiMap 的键不为唯一,因此新添加的对不会替代现有的对。

TMap

TMap 主要由两个类型定义 - 键类型和值类型 - 作为关联对存储在映射中。将这些对作为映射的元素类型参考十分便利,就像是个体对象一样。在此文档中,元素为一个键值对,而个体元素会作为元素的键或元素的值被参考。元素类型实际上是一个 TPair< KeyType, ElementType >,但它很少需要直接参考 TPair 类型。

和 TArray 一样,TMap 是同构容器,因此其所有元素完全为相同类型。TMap 也是值类型,支持常规复制、赋值和析构函数操作,以及其元素较强的所有权。映射被销毁时,其元素也将被销毁。键类型和值类型也必须为值类型。

TMap 是散列容器,意味着键类型必须支持 GetTypeHash 函数并提供一个运算符 ==,对键的相等性进行对比。稍后将详细讨论散列。

TMap 还可通过任选分配器控制内存分配行为。标准 UE4 分配器(如 FHeapAllocator、TInlineAllocator)无法被用作 TMap 的分配器。应使用标准 UE4 分配器进行散列和元素存储,而不使用定义映射使用散列桶数量的集分配器。欲知详情,请查阅 TSetAllocator。

最终的 TMap 模板参数为 KeyFuncs,它将告知映射如何从元素类获得键、如何对比两个键的相等性、如何散列键。它们默认只返回引用到键,使用运算符 == 对比相等性,使用非成员 GetTypeHash 函数进行散列。如您的键类型支持这些函数,它将作为映射键使用,无需提供自定义 KeyFuncs。

与 TArray 不同,内存中 TMap 元素的相对排序不可被依赖,元素上迭代返回的顺序可能与它们的添加顺序不同。元素在内存中不太可能被持续排列。映射的备份数据结构是稀疏阵列,带有洞。元素从映射移除后,稀疏阵列中将出现洞。之后添加的元素将填充这些洞。然而,即使 TMap 不移动元素填充洞穴,指向映射元素的指针仍然可能被无效化,因为整体存储为满时添加新元素会重新对整体存储进行分配。

 

创建并填充映射

可如此创建 TMap:

TMap<int32, FString> FruitMap;

这会创建一个空白的 TMap,把整数映射到字符串。我们指定的并非是分配器或 KeyFuncs,因此映射将执行标准堆分配;使用 == 对键(int32)进行对比,并使用 GetTypeHash 进行散列。此时尚未分配内存。

填入映射的标准方法是使用 Add 函数并提供一个键和值:


 
  1. FruitMap.Add(5, TEXT("Banana"));

  2. FruitMap.Add(2, TEXT("Grapefruit"));

  3. FruitMap.Add(7, TEXT("Pineapple"));

  4. // FruitMap == [

  5. // { Key:5, Value:"Banana" },

  6. // { Key:2, Value:"Grapefruit" },

  7. // { Key:7, Value:"Pineapple" }

  8. // ]

此处列出的元素按插入排序,但这些元素的排序不存在绝对保证。对于新映射而言,它们可能以插入排序。但映射受支配的插入和移除越多,新元素不出现在末端的可能性越大。

这不是 TMultiMap,因此键不保证为唯一。如尝试添加复制键,会发生以下情况:


 
  1. FruitMap.Add(2, TEXT("Pear"));

  2. // FruitMap == [

  3. // { Key:5, Value:"Banana" },

  4. // { Key:2, Value:"Pear" },

  5. // { Key:7, Value:"Pineapple" }

  6. // ]

映射仍然包含 3 个元素,但之前键值为 2 的“Grapefruit”已被“Pear”替代。

Add 函数被重载,以接受不带值的键。如只提供了一个键,数值将被默认构建:


 
  1. FruitMap.Add(4);

  2. // FruitMap == [

  3. // { Key:5, Value:"Banana" },

  4. // { Key:2, Value:"Pear" },

  5. // { Key:7, Value:"Pineapple" },

  6. // { Key:4, Value:"" }

  7. // ]

和 TArray 一样,我们还可使用 Emplace 代替 Add,避免插入映射时创建出临时文件:


 
  1. FruitMap.Emplace(3, TEXT("Orange"));

  2. // FruitMap == [

  3. // { Key:5, Value:"Banana" },

  4. // { Key:2, Value:"Pear" },

  5. // { Key:7, Value:"Pineapple" },

  6. // { Key:4, Value:"" },

  7. // { Key:3, Value:"Orange" }

  8. // ]

在此,两个参数分别被直接传到键类型和值类型的构建函数。这对此处的 int32 并无真正效果,但它能避免创建值的临时 FString。和 TArray 不同,只可通过单一参数构建函数将元素安放到映射中。

使用 Append 函数进行合并即可插入来自另一个映射的所有元素:


 
  1. TMap<int32, FString> FruitMap2;

  2. FruitMap2.Emplace(4, TEXT("Kiwi"));

  3. FruitMap2.Emplace(9, TEXT("Melon"));

  4. FruitMap2.Emplace(5, TEXT("Mango"));

  5. FruitMap.Append(FruitMap2);

  6. // FruitMap == [

  7. // { Key:5, Value:"Mango" },

  8. // { Key:2, Value:"Pear" },

  9. // { Key:7, Value:"Pineapple" },

  10. // { Key:4, Value:"Kiwi" },

  11. // { Key:3, Value:"Orange" },

  12. // { Key:9, Value:"Melon" }

  13. // ]

此处生成的映射和使用 Add/Emplace 进行单个添加相等,因此来自源映射的复制键会替代目标映射中的键。

迭代

TMap 的迭代与 TArray 相似。可使用 C++ 的 ranged-for 功能,注意元素类型是 TPair:


 
  1. for (auto& Elem :FruitMap)

  2. {

  3. FPlatformMisc::LocalPrint(

  4. *FString::Printf(

  5. TEXT("(%d, \"%s\")\n"),

  6. Elem.Key,

  7. *Elem.Value

  8. );

  9. }

  10. // Output:

  11. // (5, "Mango")

  12. // (2, "Pear")

  13. // (7, "Pineapple")

  14. // (4, "Kiwi")

  15. // (3, "Orange")

  16. // (9, "Melon")

映射还提供其自身的迭代器类型,以便对迭代进行更直接的控制。CreateIterator 函数提供对元素的读写访问,CreateConstIterator 函数提供只读访问。迭代器对象自身提供 Key() 和 Value() 函数进行键和值访问。代码中可使用任意形式:


 
  1. for (auto It = FruitMap.CreateConstIterator(); It; ++It)

  2. {

  3. FPlatformMisc::LocalPrint(

  4. *FString::Printf(

  5. TEXT("(%d, \"%s\")\n"),

  6. It.Key(), // same as It->Key

  7. *It.Value() // same as *It->Value

  8. );

  9. }

查询

使用 Num 函数可询问映射保存的元素数量:


 
  1. int32 Count = FruitMap.Num();

  2. // Count == 6

可结合键使用索引运算符 [],获得给定键相关数值的引用。在非常量映射上调用运算符 [] 将返回非常量引用,而在常量映射上调用将返回常量引用。如键不存在,将出现断言:


 
  1. FString Val7 = FruitMap[7];

  2. // Val7 == "Pineapple"

  3. FString Val8 = FruitMap[8]; // assert!

可使用 Contains 函数进行检查,确定特定键是否存在于映射中:


 
  1. bool bHas7 = FruitMap.Contains(7);

  2. bool bHas8 = FruitMap.Contains(8);

  3. // bHas7 == true

  4. // bHas8 == false

多数情况下,查找元素无需知晓键是否存在。使用后面带操作符 [] 的 Contains 函数将进行键的双重查找,最好不要进行此操作。使用 Find函数可进行单一查找,返回指向找到元素数值的指针,而非引用;键不存在时,将返回 null。


 
  1. FString* Ptr7 = FruitMap.Find(7);

  2. FString* Ptr8 = FruitMap.Find(8);

  3. // *Ptr7 == "Pineapple"

  4. // Ptr8 == nullptr

如在常量映射上调用,返回的指针也将为常量。

FindOrAdd 函数将搜索给定键并返回引用到关联值;如键不存在,则在返回引用前将添加默认构建的值。因可能需要添加,此函数无法在常量映射上被调用:


 
  1. FString& Ref7 = FruitMap.FindOrAdd(7);

  2. // Ref7 == "Pineapple"

  3. // FruitMap == [

  4. // { Key:5, Value:"Mango" },

  5. // { Key:2, Value:"Pear" },

  6. // { Key:7, Value:"Pineapple" },

  7. // { Key:4, Value:"Kiwi" },

  8. // { Key:3, Value:"Orange" },

  9. // { Key:9, Value:"Melon" }

  10. // ]

  11. FString& Ref8 = FruitMap.FindOrAdd(8);

  12. // Ref8 == ""

  13. // FruitMap == [

  14. // { Key:5, Value:"Mango" },

  15. // { Key:2, Value:"Pear" },

  16. // { Key:7, Value:"Pineapple" },

  17. // { Key:4, Value:"Kiwi" },

  18. // { Key:3, Value:"Orange" },

  19. // { Key:9, Value:"Melon" },

  20. // { Key:8, Value:"" }

  21. // ]

注意:如已发生重新分配,此处的 Ref7 引用可能已被 FruitMap.FindOrAdd(8) 的调用无效化。

无视 FindRef 函数的名称,因为它搜索键返回的是值,而非引用。如找到键,则返回关联值的副本;如未找到,则返回默认构建值类型。这会导致和 FindOrAdd 相似的行为,但因 FindRef 函数返回的是值而非引用,映射将不会被修改,因此可在常量对象上被调用:


 
  1. FString Val7 = FruitMap.FindRef(7);

  2. FString Val6 = FruitMap.FindRef(6);

  3. // Val7 == "Pineapple"

  4. // Val6 == ""

  5. // FruitMap == [

  6. // { Key:5, Value:"Mango" },

  7. // { Key:2, Value:"Pear" },

  8. // { Key:7, Value:"Pineapple" },

  9. // { Key:4, Value:"Kiwi" },

  10. // { Key:3, Value:"Orange" },

  11. // { Key:9, Value:"Melon" },

  12. // { Key:8, Value:"" }

  13. // ]

FindKey 函数允许执行逆向查找(找到键给定值)。使用该函数时需注意,因为值和键不同,不会被散列。因此键查找是线性操作。此外,数值不保证为唯一。因此,如映射包含重复值,键返回的特定值为任意的。


 
  1. const int32* KeyMangoPtr = FruitMap.FindKey(TEXT("Mango"));

  2. const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat"));

  3. // *KeyMangoPtr == 5

  4. // KeyKumquatPtr == nullptr

GenerateKeyArray 和 GenerateValueArray 函数分别允许以全部键和值的副本对阵列进行填入。在两种情况下,被传递的阵列在填入前会被清空,因此元素的生成数量将始终等于映射中的元素数量。


 
  1. TArray<int32> FruitKeys;

  2. TArray<FString> FruitValues;

  3. FruitKeys.Add(999);

  4. FruitKeys.Add(123);

  5. FruitMap.GenerateKeyArray (FruitKeys);

  6. FruitMap.GenerateValueArray(FruitValues);

  7. // FruitKeys == [ 5,2,7,4,3,9,8 ]

  8. // FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange",

  9. // "Melon","" ]

移除

使用 Remove 函数并提供要删除的元素键即可将元素从映射移除:


 
  1. FruitMap.Remove(8);

  2. // FruitMap == [

  3. // { Key:5, Value:"Mango" },

  4. // { Key:2, Value:"Pear" },

  5. // { Key:7, Value:"Pineapple" },

  6. // { Key:4, Value:"Kiwi" },

  7. // { Key:3, Value:"Orange" },

  8. // { Key:9, Value:"Melon" }

  9. // ]

移除元素将在数据结构(在 Visual Studio 的观察窗口中可视化映射时可看到)中留下洞,但为保证清晰性,此处将忽略洞。

FindAndRemoveChecked 函数可用于移除元素,并返回关联值。名称中的 checked 部分意味着将检查键是否存在。如不存在,则断言:


 
  1. FString Removed7 = FruitMap.FindAndRemoveChecked(7);

  2. // Removed7 == "Pineapple"

  3. // FruitMap == [

  4. // { Key:5, Value:"Mango" },

  5. // { Key:2, Value:"Pear" },

  6. // { Key:4, Value:"Kiwi" },

  7. // { Key:3, Value:"Orange" },

  8. // { Key:9, Value:"Melon" }

  9. // ]

  10.  
  11. FString Removed8 = FruitMap.FindAndRemoveChecked(8); // assert!

RemoveAndCopyValue 函数作用相似,但会引用将被传出的值类型,并返布尔型说明键是否已找到。它可结合缺失键使用,不会出现运行时错误。如未找到键,调用将返回 false,传递对象和映射保持不变:


 
  1. FString Removed;

  2. bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);

  3. // bFound2 == true

  4. // Removed == "Pear"

  5. // FruitMap == [

  6. // { Key:5, Value:"Mango" },

  7. // { Key:4, Value:"Kiwi" },

  8. // { Key:3, Value:"Orange" },

  9. // { Key:9, Value:"Melon" }

  10. // ]

  11. bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);

  12. // bFound8 == false

  13. // Removed == "Pear", i.e. unchanged

  14. // FruitMap == [

  15. // { Key:5, Value:"Mango" },

  16. // { Key:4, Value:"Kiwi" },

  17. // { Key:3, Value:"Orange" },

  18. // { Key:9, Value:"Melon" }

  19. // ]

最后,使用 Empty 函数可将所有元素移除:


 
  1. TMap<int32, FString> FruitMapCopy = FruitMap;

  2. // FruitMapCopy == [

  3. // { Key:5, Value:"Mango" },

  4. // { Key:4, Value:"Kiwi" },

  5. // { Key:3, Value:"Orange" },

  6. // { Key:9, Value:"Melon" }

  7. // ]

  8.  
  9. FruitMapCopy.Empty();

  10. // FruitMapCopy == []

和 TArray 一样,Empty 接受任选的 slack 值。以给定数量的元素重新填入映射时,此值可用于优化。

排序

可对 TMap 进行临时排序。映射上的下次迭代将以排序顺序展示元素,之后对映射进行的修改可能导致映射重新排序。排序并不稳定,因此相等元素可能以各种排列方式出现。

使用 KeySort 和 ValueSort 函数可分别按键和值进行排序,两个函数均接受二元谓词指定排序顺序:


 
  1. FruitMap.KeySort([](int32 A, int32 B) {

  2. return A > B; // sort keys in reverse

  3. });

  4. // FruitMap == [

  5. // { Key:9, Value:"Melon" },

  6. // { Key:5, Value:"Mango" },

  7. // { Key:4, Value:"Kiwi" },

  8. // { Key:3, Value:"Orange" }

  9. // ]

  10.  
  11. FruitMap.ValueSort([](const FString& A, const FString& B) {

  12. return A.Len() < B.Len(); // sort strings by length

  13. });

  14. // FruitMap == [

  15. // { Key:4, Value:"Kiwi" },

  16. // { Key:5, Value:"Mango" },

  17. // { Key:9, Value:"Melon" },

  18. // { Key:3, Value:"Orange" }

  19. // ]

运算符

和 TArray 一样,TMap 是常规值类型,可通过标准复制构建函数或赋值运算符进行复制。因映射严格拥有其元素,映射复制为深,因此新映射将拥有其自身的元素副本:


 
  1. TMap<int32, FString> NewMap = FruitMap;

  2. NewMap[5] = "Apple";

  3. NewMap.Remove(3);

  4. // FruitMap == [

  5. // { Key:4, Value:"Kiwi" },

  6. // { Key:5, Value:"Mango" },

  7. // { Key:9, Value:"Melon" },

  8. // { Key:3, Value:"Orange" }

  9. // ]

  10. // NewMap == [

  11. // { Key:4, Value:"Kiwi" },

  12. // { Key:5, Value:"Apple" },

  13. // { Key:9, Value:"Melon" }

  14. // ]

TMap 还支持移动语意。使用 MoveTemp 函数可调用此语意。在移动后,源映射将保证为空:


 
  1. FruitMap = MoveTemp(NewMap);

  2. // FruitMap == [

  3. // { Key:4, Value:"Kiwi" },

  4. // { Key:5, Value:"Apple" },

  5. // { Key:9, Value:"Melon" }

  6. // ]

  7. // NewMap == []

Slack

TMap 也拥有 slack 的概念,可用于优化映射的填入。Reset 与 Empty() 调用作用相似,但不会释放元素之前使用的内存。


 
  1. FruitMap.Reset();

  2. // FruitMap == [<invalid>, <invalid>, <invalid>]

此处映射按照 Empty 相同的方式进行清空,但用于储存的内存不会被释放,仍为 slack。

TMap 不会像 TArray::Max() 一样提供检查预分配元素的数量,但仍然支持预分配 slack。Reserve 函数可用于在添加之前预分配特定数量元素的 slack。


 
  1. FruitMap.Reserve(10);

  2. for (int32 i = 0; i != 10; ++i)

  3. {

  4. FruitMap.Add(i, FString::Printf(TEXT("Fruit%d"), i));

  5. }

  6. // FruitMap == [

  7. // { Key:9, Value:"Fruit9" },

  8. // { Key:8, Value:"Fruit8" },

  9. // ...

  10. // { Key:1, Value:"Fruit1" },

  11. // { Key:0, Value:"Fruit0" }

  12. // ]

注意:Slack 会导致新元素以倒序被添加。这是为什么不可信赖映射中元素排序的原因。

Shrink 函数和 TArray 中相等函数的相同之处是 - 它将从容器末端移除被废弃的 slack。然而,因为 TMap 允许其数据结构中存在洞,这只会从遗留在结构末端的洞上移除 slack。


 
  1. for (int32 i = 0; i != 10; i += 2)

  2. {

  3. FruitMap.Remove(i);

  4. }

  5. // FruitMap == [

  6. // { Key:9, Value:"Fruit9" },

  7. // <invalid>,

  8. // { Key:7, Value:"Fruit7" },

  9. // <invalid>,

  10. // { Key:5, Value:"Fruit5" },

  11. // <invalid>,

  12. // { Key:3, Value:"Fruit3" },

  13. // <invalid>,

  14. // { Key:1, Value:"Fruit1" },

  15. // <invalid>

  16. // ]

  17. FruitMap.Shrink();

  18. // FruitMap == [

  19. // { Key:9, Value:"Fruit9" },

  20. // <invalid>,

  21. // { Key:7, Value:"Fruit7" },

  22. // <invalid>,

  23. // { Key:5, Value:"Fruit5" },

  24. // <invalid>,

  25. // { Key:3, Value:"Fruit3" },

  26. // <invalid>,

  27. // { Key:1, Value:"Fruit1" }

  28. // ]

注意:只有一个无效元素已从 Shrink 调用移除,因为末端只有一个洞。Compact 函数可用于在缩小前移除所有洞。


 
  1. FruitMap.Compact();

  2. // FruitMap == [

  3. // { Key:9, Value:"Fruit9" },

  4. // { Key:7, Value:"Fruit7" },

  5. // { Key:5, Value:"Fruit5" },

  6. // { Key:3, Value:"Fruit3" },

  7. // { Key:1, Value:"Fruit1" },

  8. // <invalid>,

  9. // <invalid>,

  10. // <invalid>,

  11. // <invalid>

  12. // ]

  13. FruitMap.Shrink();

  14. // FruitMap == [

  15. // { Key:9, Value:"Fruit9" },

  16. // { Key:7, Value:"Fruit7" },

  17. // { Key:5, Value:"Fruit5" },

  18. // { Key:3, Value:"Fruit3" },

  19. // { Key:1, Value:"Fruit1" }

  20. // ]

KeyFuncs

只要类型拥有一个运算符 == 和一个非成员 GetTypeHash 重载,则可被用作 TMap 的一个 KeyType,无需进行任何修改。然而,不便于重载这些函数时可将类型作为键使用。在这些情况下,您可提供自定义的 KeyFuncs

KeyFuncs 需要 2 个 typedefs 和 3 个静态函数的定义:

  • KeyInitType - 用于传递键

  • ElementInitType - 用于传递元素

  • KeyInitType GetSetKey(ElementInitType Element) - 返回元素的键。

  • bool Matches(KeyInitType A, KeyInitType B) - 返回 A 和 B 是否相等。

  • uint32 GetKeyHash(KeyInitType Key) - 返回键的散列值。

KeyInitType 和 ElementInitType 是键类型和元素类型普通传递惯例的 typedefs。它们通常为浅显类型的一个值和非浅显类型的一个常量引用。需牢记:映射的元素类型为 TPair。

自定义 KeyFuncs 的实例如下:


 
  1. struct FMyStruct

  2. {

  3. // String which identifies our key

  4. FString UniqueID;

  5.  
  6. // Some state which doesn't affect struct identity

  7. float SomeFloat;

  8.  
  9. explicit FMyStruct(float InFloat)

  10. :UniqueID (FGuid::NewGuid().ToString())

  11. , SomeFloat(InFloat)

  12. {

  13. }

  14. };

  15. template <typename ValueType>

  16. struct TMyStructMapKeyFuncs :

  17. BaseKeyFuncs<

  18. TPair<FMyStruct, ValueType>,

  19. FString

  20. >

  21. {

  22. private:

  23. typedef BaseKeyFuncs<

  24. TPair<FMyStruct, ValueType>,

  25. FString

  26. > Super;

  27.  
  28. public:

  29. typedef typename Super::ElementInitType ElementInitType;

  30. typedef typename Super::KeyInitType KeyInitType;

  31.  
  32. static KeyInitType GetSetKey(ElementInitType Element)

  33. {

  34. return Element.Key.UniqueID;

  35. }

  36.  
  37. static bool Matches(KeyInitType A, KeyInitType B)

  38. {

  39. return A.Compare(B, ESearchCase::CaseSensitive) == 0;

  40. }

  41.  
  42. static uint32 GetKeyHash(KeyInitType Key)

  43. {

  44. return FCrc::StrCrc32(*Key);

  45. }

  46. };

此处类型拥有一个唯一辨识符将作为其状态的部分,但还存在一些对辨识其身份没有帮助的其他状态。GetTypeHash 和运算符 == 不适合在此使用。因为它会使运算符 == 忽略部分状态,而且 GetTypeHash 需要和运算符 == 相匹配;如运算符 == 已被正确定义,则无法进行。然而,出于在映射中识别此类型的目的,我们可只使用状态的 UniqueID。

最后,我们将继承 BaseKeyFuncs,因为它有助于为我们进行一些内容的定义,包括 KeyInitType 和 ElementInitType。直接将它们从 Super 拉到派生类中,以便在实现的剩余部分中使用。

BaseKeyFuncs 接受两个模板参数:映射的元素类和键的类型(从 GetSetKey 返回的对象)。和所有的映射一样,元素类型是 TPair,接受 FMyStruct 作为其 KeyType、接受 TMyStructMapKeyFuncs 的模板参数作为其 ValueType。我们使替代 KeyFuncs 成为模板,使 ValueType 以每个函数为基础进行指定;每次创建 FMyStruct 上有键的 TMap 时,均无需对新的 KeyFuncs 进行定义。第二个 BaseKeyFuncs 参数是键类型,不要将其与 TPair 的“KeyType”混淆。此 KeyType 存储在元素的键区中。需要将 FMyStruct::UniqueID 作为键使用,因此我们需要在此处指定 FString。

然后对所需要的 3 个 KeyFuncs 静态函数进行指定。第一个是 GetSetKey,被给定一个元素类型,将返回键。我们的元素类型是 TPair,key 是 UniqueID,因此我们将直接返回。

第二个静态函数是 Matches,它接受两个元素的键(已使用 GetSetKey 从元素类型中提取),然后将它们进行比较,确定是否为相等。FString 的运算符 == 不区分大小写,但我们需要执行区分大小写的搜索,因此应结合适当的选项使用 FString::Compare 函数。

最后,GetKeyHash 静态函数接受提取键,并返回一个它的散列值。再次提醒,FString 的 GetTypeHash 行为会忽略大小写,因此我们调用一个区分大小写的 FCrc 函数进行散列的计算。

现在我们可使用新 KeyFuncs 创建一个 TMap。我们还需要提供一个分配器,因为 KeyFuncs 参数在最后,但我们可对默认进行替代:


 
  1. TMap<

  2. FMyStruct,

  3. int32,

  4. FDefaultSetAllocator,

  5. TMyStructMapKeyFuncs<int32>

  6. > MyMapToInt32;

  7.  
  8. // Add some elements

  9. MyMapToInt32.Add(FMyStruct(3.14f), 5);

  10. MyMapToInt32.Add(FMyStruct(1.23f), 2);

  11.  
  12. // MyMapToInt32 == [

  13. // {

  14. // Key:{

  15. // UniqueID:"D06AABBA466CAA4EB62D2F97936274E4",

  16. // SomeFloat:3.14f

  17. // },

  18. // Value:5

  19. // },

  20. // {

  21. // Key:{

  22. // UniqueID:"0661218447650259FD4E33AD6C9C5DCB",

  23. // SomeFloat:1.23f

  24. // },

  25. // Value:5

  26. // }

  27. // ]

提供自己的 KeyFuncs 时需注意:TMap 假定对比相等的两个项目使用 KeyFuncs::Matches 从 KeyFuncs::GetKeyHash 返回相同的值。此外,如修改现有映射元素的键时会改变任意一个这些函数的结果,则会被理解为未定义行为,因为这会无效化 TMap 的内部散列。使用默认 KeyFuncs 时,这些规则还会应用到运算符 == 和 GetKeyHash 的重载。

杂项

CountBytes 和 GetAllocatedSize 函数用于估计阵列当前应用的内存量。CountBytes 接受 FArchive,GetAllocatedSize 可被直接调用。它们常用于统计报告。

Dump 函数接受 FOutputDevice 并写出关于映射内容的部分实现信息。它通常用于调试。

 

 

以上转自
https://blog.csdn.net/zhaoguanghui2012/article/details/80706514

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!