学习虚幻C++开发日志——TSet

devtools/2024/10/20 14:22:36/

TSet

官方文档:虚幻引擎中的Set容器 | 虚幻引擎 5.5 文档 | Epic Developer Community (epicgames.com)

TSet 是通过对元素求值的可覆盖函数,使用数据值本身作为键,而不是将数据值与独立的键相关联。

默认情况下,TSet 不支持重复的键,但使用模板参数可激活此行为。

TSet 是一种快速容器类,用于在排序不重要的情况下存储唯一元素。TSet 可以非常快速地添加、查找和删除元素(恒定时间)。在大多数情况下,只需要一种参数——元素类型。但是,TSet 可配置各种模板参数来改变其行为,使其更全面。除了可指定从 DefaultKeyFuncs 的派生结构体来提供散列功能,还可允许集合中的多个键拥有相同的值。它和其它容器类一样,可设置自定义内存分配器来存储数据。

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

TSet 使用散列,即如果给出了 KeyFuncs 模板参数,该参数会告知集合如何从某个元素确定键,如何比较两个键是否相等,如何对键进行散列,以及是否允许重复键。它们默认只返回对键的引用,使用 运算符== 对比相等性,使用非成员函数 GetTypeHash 进行散列。默认情况下,集合中不允许有重复的键。如果您的键类型支持这些函数,则可以将其用作集合键,无需提供自定义 KeyFuncs。要写入自定义 KeyFuncs,可扩展 DefaultKeyFuncs 结构体。

最后,TSet 可通过任选分配器控制内存分配行为。标准虚幻引擎4(UE4)分配器(如 FHeapAllocator 和 TInlineAllocator)不能用作 TSet 的分配器。实际上,TSet 使用集合分配器,该分配器可定义集合中使用的散列桶数量以及用于存储元素的标准UE4分配器。

与 TArray 不同的是,内存中 TSet 元素的相对排序既不可靠也不稳定,对这些元素进行迭代很可能会使它们返回的顺序和它们添加的顺序有所不同。这些元素也不太可能在内存中连续排列。集合中的后台数据结构是稀疏数组(在数组中有空位。从集合中移除元素时,稀疏数组中会出现空位)。将新的元素添加到阵列可填补这些空位。

但是,即便 TSet 不会打乱元素来填补空位,指向集元素的指针仍然可能失效,因为如果存储器被填满,又添加了新的元素,整个存储可能会重新分配。

1.创建和填充集合

创建一个MapActor类并继承于Actor,其与TArray创建方法一样就不一一详细介绍;

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void InitSet();

源文件增添代码:

void ASetActor::InitSet()
{TSet<FString> FruitSet;//此处的元素按插入顺序排列,但不保证这些元素在内存中实际保留此排序。//如果是新集合,可能会保留插入排序,但插入和删除的次数越多,新元素不出现在末尾的可能性越大。FruitSet.Add(TEXT("Banana"));FruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));// FruitSet == [ "Banana", "Grapefruit", "Pineapple" ]FruitSet.Add(TEXT("Pear"));FruitSet.Add(TEXT("Banana"));//此处与上面的键重复因此覆盖了,但此处会触发扩容// FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear" ]// Note:Only one banana entry.//此处,参数直接传递给键类型的构造函数。这可以避免为该值创建临时 FString。//与 TArray 不同的是,只能使用单一参数构造函数将元素放到集合中。FruitSet.Emplace(TEXT("Orange"));//追加元素,用Emplace函数代替Add避免插入集合时创造临时文件// FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange" ]TSet<FString> FruitSet2;FruitSet2.Emplace(TEXT("Kiwi"));FruitSet2.Emplace(TEXT("Melon"));FruitSet2.Emplace(TEXT("Mango"));FruitSet2.Emplace(TEXT("Orange"));FruitSet.Append(FruitSet2);// FruitSet == [ "Banana", "Grapefruit", "Pineapple", "Pear", "Orange", "Kiwi", "Melon", "Mango" ]
}

注意元素以及Num的变化,从而去理解TSet键的原理 (使用数据值本身作为键)

2.编辑UPROPERTY TSet

如果用 UPROPERTY 宏和一个可编辑的关键词(EditAnywhereEditDefaultsOnly 或 EditInstanceOnly)标记 TSet,则可在虚幻编辑器中添加和编辑元素。

Actor类头文件增添代码:

public:UPROPERTY(BlueprintReadWrite,EditAnyWhere,Category = SetExample)TSet<FString> MyFruitSet;

在蓝图可编辑操作

此处为FString,但其也可以放结构体

3.迭代

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void LoopSet();

源文件增添代码:

void ASetActor::LoopSet()
{TSet<FString> FruitSet;FruitSet.Add(TEXT("Banana"));FruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));//依次输出元素for (auto& Elem :FruitSet)//此处的auto可换为FString,其本质时自动推导类型{FPlatformMisc::LocalPrint(*FString::Printf(TEXT(" \"%s\"\n"),*Elem));}//创建迭代器(如为CreateConstIterators 函数则为常量迭代器)for (auto It = FruitSet.CreateIterator(); It; ++It){//注意后面*(*It),第一个*是把FString类型变成TCHAR,第二个是指针,因为迭代器It是地址FPlatformMisc::LocalPrint(*FString::Printf(TEXT("(%s)\n"),*(*It)));}for (auto It = FruitSet.CreateConstIterator(); It; ++It){FPlatformMisc::LocalPrint(*FString::Printf(TEXT("(%s)\n"),*(*It)));}
}

4.查询

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void QuerySet();

源文件增添代码:

void ASetActor::QuerySet()
{TSet<FString> FruitSet;bool bHave=false;FruitSet.Add(TEXT("Banana"),&bHave);//此处bHave为falseFruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));FruitSet.Add(TEXT("Banana"),&bHave);//此处使得bHave为trueint32 Count = FruitSet.Num();// Count == 3//要确定集合是否包含特定元素,可按如下所示调用 Contains 函数bool bHasBanana = FruitSet.Contains(TEXT("Banana"));bool bHasLemon = FruitSet.Contains(TEXT("Lemon"));// bHasBanana == true// bHasLemon == false//使用 FSetElementId 结构体可查找集合中某个键的索引。FSetElementId SetElementId=Fruit.Add(TEXT("Water"));//FSetElementId为标识符,此处SetElementId={Index=3}FruitSet[SetElementId]+=TEXT("Modify");//此处将Index为3的元素进行修改为"WaterModify"//使用 Find 函数查找一次即可完成这些行为。//如果集合中包含该键,Find 将返回指向元素数值的指针。如果映射不包含该键,则返回null。对常量集合调用Find,返回的指针也将为常量。FString* PtrBanana = FruitSet.Find(TEXT("Banana"));FString* PtrLemon = FruitSet.Find(TEXT("Lemon"));// *PtrBanana == "Banana"//  PtrLemon == nullptr//Array 函数会返回一个 TArray,其中填充(覆盖,即使有元素在内)了 TSet 中每个元素的一份副本。因此元素的生成数量将始终等于集合中的元素数量TArray<FString> FruitArray = FruitSet.Array();//用Array函数转换为TArray数组
}

 5.移除

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void RemoveSet();

源文件增添代码:

void ASetActor::RemoveSet()
{TSet<FString> FruitSet;FruitSet.Reserve(4);FruitSet.Add(TEXT("Banana"));FruitSet.Add(TEXT("Grapefruit"));FruitSet.Add(TEXT("Pineapple"));FruitSet.Add(TEXT("GG"));FruitSet.Add(TEXT("HH"));FruitSet.Add(TEXT("JJ"));//Remove函数有两种使用参数方法FruitSet.Remove(FSetElementId::FromInterger(0));//通过索引移除第一个元素//Remove函数会返回已删除元素的数量。int32 GGNum=FruitSet.remove(TEXT("GG"));//GGNum=1int32 MMNum=FruitSet.remove(TEXT("MM"));//MMNum=0TSet<FString> FruitSet1=FruitSet;FruitSet1.Reset();//将集合中的所有元素移除,但内存空间还在TSet<FString> FruitSet2=FruitSet;FruitSet2.Empty(0);//此操作是将元素及其内存空间都删除
}

6.排序&运算符&Slack

6.1排序

TSet 可以排序。排序后,迭代集合会以排序的顺序显示元素,但下次修改集合时,排序可能会发生变化。由于排序不稳定,可能按任何顺序显示集合中支持重复键的等效元素。

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void SortSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};FruitSet.Sort([](const FString& A, const FString& B) {return A > B; // sort by reverse-alphabetical order});// FruitSet == [ "Pear", "Orange", "Melon", "Mango", "Kiwi", "Grapefruit" ] (order is temporarily guaranteed)//Sort 函数使用指定排序顺序的二进制谓词FruitSet.Sort([](const FString& A, const FString& B) {return A.Len() < B.Len(); // sort strings by length, shortest to longest});// FruitSet == [ "Pear", "Kiwi", "Melon", "Mango", "Orange", "Grapefruit" ] (order is temporarily guaranteed)
}

6.2运算符

其和 TArray 一样,TSet 是常规值类型,可通过标准复制构造函数赋值运算符进行复制。因为集合严格拥有其元素,复制集合的操作是深层的,所以新集合将拥有其自身的元素副本。

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void OpeatorSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};TSet<FString> NewSet = FruitSet;//在新的进行增删改查NewSet.Add(TEXT("Apple"));NewSet.Remove(TEXT("Pear"));
}
//在移除目录处也有提及

6.3Slack 

其用法与TMap容器大致相似

Actor类头文件增添代码:

public:UFUNCTION(BlueprintCallable)void SlackSet();

源文件增添代码:

void ASetActor::SortSet()
{TSet<FString>FruitSet={ "Orange","Pear", "Melon", "Grapefruit", "Mango", "Kiwi"};//Reset函数可在不取消任何内存的情况下移除集合中的所有元素,从而产生slackFruitSet.Reset();FruitSet.Reserve(10);//在原基础上追加预分配10个内存for (int32 i = 0; i < 10; ++i){FruitSet.Add(FString::Printf(TEXT("Fruit%d"), i));}
// FruitSet == [ "Fruit9", "Fruit8", "Fruit7" ..."Fruit2", "Fruit1", "Fruit0" ]// Remove every other element from the set.for (int32 i = 0; i < 10; i += 2){FruitSet.Remove(FSetElementId::FromInteger(i));}
// FruitSet == ["Fruit8", <invalid>, "Fruit6", <invalid>, "Fruit4", <invalid>, "Fruit2", <invalid>, "Fruit0", <invalid> ]//Shrink裁剪元素FruitSet.Shrink();//注意此处数组Max值为10 
// FruitSet == ["Fruit8", <invalid>, "Fruit6", <invalid>, "Fruit4", <invalid>, "Fruit2", <invalid>, "Fruit0" ]//CompactStable函数压缩元素,Compact函数则可能改变排序FruitSet.CompactStable();// FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0", <invalid>, <invalid>, <invalid>, <invalid> ]FruitSet.Shrink();// FruitSet == ["Fruit8", "Fruit6", "Fruit4", "Fruit2", "Fruit0" ]
}

 


http://www.ppmy.cn/devtools/127307.html

相关文章

《深度学习》OpenCV EigenFaces算法 人脸识别

目录 一、EigenFaces算法 1、什么是EigenFaces算法 2、原理 3、实现步骤 1&#xff09;数据预处理 2&#xff09;特征提取 3&#xff09;构建模型 4&#xff09;识别 4、优缺点 1&#xff09;优点 2&#xff09;缺点 二、案例实现 1、完整代码 运行结果&#xff…

【星闪开发连载】WS63E模组的速度测试

目录 ​编辑 引言 程序工作原理 客户端 服务器端 测试记录 近距离测试 相邻两个房间之间的测试 相隔一个房间的两个房间之间的测试 结语 引言 今天终于又有点时间了&#xff0c;来测试一下星闪的数据传输速度。前面的博文已经分析了星闪的传输示例sle_uuid_client和…

Redis高可用与分布式方案实战指南

Redis作为一种高性能的内存数据库&#xff0c;在实际应用中常常需要考虑高可用性和分布式架构。本文将详细介绍Redis的高可用解决方案&#xff08;如Redis Sentinel&#xff09;和分布式方案&#xff08;如Redis Cluster&#xff09;&#xff0c;帮助您构建可靠且高效的Redis系…

【CTF刷题9】2024.10.19

[MoeCTF 2021]babyRCE 考点&#xff1a;关键词过滤&#xff08;绕过方法参考往期博客&#xff09; 来源&#xff1a;nssctf <?php$rce $_GET[rce]; if (isset($rce)) {if (!preg_match("/cat|more|less|head|tac|tail|nl|od|vi|vim|sort|flag| |\;|[0-9]|\*|\|\%|\&g…

S7-200 SMART 与 S7-1200 之间 TCP 通信— S7-200 SMART 作为服务器

TCP 协议通信 TCP 通信为面向连接的通信&#xff0c;需要双方都调用指令以建立连接及交换数据。S7-200 SMART 与 S7-1200 通过 TCP 通信&#xff0c;在 S7-1200 调用 T-block 指令 ( TCON, TDISCON, TSEND, TRCV ) &#xff0c;在 S7-200 SMART 调用 Open User Communication …

出现身份验证错误,无法连接到本地安全机构 顺利解决这个问题希望能帮助大家

出现身份验证错误&#xff0c;无法连接到本地安全机构&#xff0c;远程计算机&#xff1a;XX&#xff0c;这可能是由于密码过期&#xff0c;如果密码已过期请更新密码。 我们可以在系统属性中对远程进行设置&#xff0c;以解决远程桌面无法连接到本地安全机构这一问题。 步骤…

Vue3获取ref元素的几种方式

静态绑定 获取单个 dom 元素 v-for 中使用 需要注意的是&#xff0c;访问的时候&#xff0c;要确保 ref 引用值已经成功绑定上元素&#xff0c;我们可以使用以下几种方式确保获取

Linux 累加计算递归算法汇编实现

1...n可以使用公式计算&#xff0c;同时也是递归实现的很好例子&#xff0c;其c实现代码为 int f(int i) {i && (if(i-1));return i; } 其终止条件为0&#xff0c;此时i && &#xff08;if(i-1)&#xff09;表达式不成立&#xff0c;不计算if(i-1)直接返回0&…