UE4 中 TMap<>与TArray<>的全遍历,及带条件删除

本文介绍了如何在UnrealEngine4(UE4)中使用TMap和TArray数据结构进行正向和逆向遍历,并附带条件删除元素,包括注意事项和示例代码。

一、UE4 中,TMap<int32,int32> 正向遍历,带条件删除。

注意点:在调用 Remove(it->Key);之后,it 并不会自动往后移动,仍然需要调用 ++it,才会指向下一个值。

	// TMap<> 正向遍历,带条件删除。
	TMap<int32, int32> aaaa;
	aaaa.Add(1, 2);
	aaaa.Add(16, 2);
	aaaa.Add(7, 2);
	aaaa.Add(5, 2);
	aaaa.Add(26, 2);
	aaaa.Add(2, 2);
	aaaa.Add(4, 2);
	aaaa.Add(8, 2);
	aaaa.Add(6, 2);
	TMap<int32, int32>::TIterator it = aaaa.CreateIterator();
	for (; it; ++it)
	{
		int32 nn = it->Key;
		if (it->Key == 7 || it->Key == 2 || it->Key == 8)
		{
			aaaa.Remove(it->Key);
		}
	}

二、UE4 中,TArray<> 正向遍历,带条件删除。

注意点:在调用 RemoveAt(i) 之后,不能执行 i++,否则会漏掉一个元素的访问。

	// TArray<> 正向遍历,带条件删除。
	TArray<int32> ArrInt = { 3,6,1,3,7,2,9,8,6 };
	for (int32 i = 0; i < ArrInt.Num();)
<think> 我们正在讨论的是在Unreal Engine中使用TMap<FString, AActor*>作为函数参数的相关情况。根据引用[2]中关于TMap的描述,我们知道TMap是一种关联容器,其元素的顺序不可靠,且内存不连续。同时,引用[1]中提到了在反射调用函数时处理参数的方式,包括分配内存、初始化参数和拷贝返回值等。 用户询问的是当函数参数中存在TMap<FString, AActor*>类型时的使用方法、注意事项、潜在问题及解决方案。 ### 使用方法 1. **传递TMap参数**:在C++中,可以直接将TMap作为参数传递。但需要注意的是,TMap是值类型,传递时会发生复制(除非使用引用或指针)。为了提高效率,通常使用const引用传递(const TMap<...>&)以避免不必要的复制。 2. **在蓝图中使用**:如果要将TMap暴露给蓝图,需要使用UPROPERTY标记,并确保TMap的键和值类型是蓝图可识别的。FString是蓝图可识别的,但AActor*需要转换为UObject*(因为AActor继承自UObject)。因此,通常使用TMap<FString, TSoftObjectPtr<AActor>>或TMap<FString, AActor*>并确保在蓝图中正确处理(注意蓝图不能直接处理裸指针,但可以通过其他方式间接处理,如使用对象引用)。 ### 注意事项 1. **内存管理**:由于TMap内部使用稀疏数组,添加或删除元素可能导致内存重新分配(引用[2]提到),因此指向元素的指针可能会失效。在传递TMap并对其进行修改时要注意这一点。 2. **复制开销**:TMap的复制可能来较大的开销,特别是当元素很多时。因此,在函数参数中尽量使用引用传递。 3. **蓝图支持**:在蓝图中使用TMap作为函数参数时,需要确保该函数被标记为UFUNCTION,并且参数类型是蓝图支持的。例如: ```cpp UFUNCTION(BlueprintCallable) void MyFunction(const TMap<FString, AActor*>& ActorMap); ``` 但是,注意蓝图不能直接处理AActor*(因为它是一个原始指针),通常我们会使用`TSubclassOf<AActor>`或者`AActor*`作为参数时,蓝图实际上会传递一个对象引用(在蓝图中是对象变量)。对于TMap中的值类型为AActor*,蓝图会将其视为对象引用(即UObject*),但需要确保在蓝图中传递的是正确的对象。 ### 潜在问题及解决方案 1. **问题:传递到蓝图时,TMap中的AActor*可能无法被正确识别** **解决方案**:使用UPROPERTY标记,并使用蓝图友好的类型。例如,可以将AActor*改为TSoftObjectPtr<AActor>,这样在蓝图中可以引用到具体的Actor(特别是当Actor还未加载时)。或者,如果只在同一个关卡中使用,可以传递TMap<FString, AActor*>,并确保在蓝图中通过其他方式(如获取所有Actor)来填充这个映射。 2. **问题:在反射调用时(如通过UFunction调用),处理TMap参数复杂** 引用[1]中提到,反射调用需要分配参数内存并初始化。由于TMap是一个复杂的容器,处理起来比普通类型更复杂。 **解决方案**:确保在反射系统中注册该类型。UE4的反射系统支持TMap,但需要确保键和值类型也是可反射的。对于自定义类型,可能需要额外的步骤。另外,在传递TMap给UFunction时,需要确保该UFunction的参数类型正确声明。 3. **问题:TMap的迭代顺序不稳定**(引用[2]) 如果在函数中需要依赖特定的顺序,那么使用TMap就不合适。 **解决方案**:如果需要有序,考虑使用TArray<std::tuple<FString, AActor*>>或者TMap的变体,或者单独维护一个键的数组来记录顺序。 4. **问题:多线程访问** 如果函数可能在多线程环境下被调用,而TMap不是线程安的。 **解决方案**:使用锁(如FCriticalSection)来保护对TMap的访问,或者使用线程安的容器(如TConcurrentMap,但需要注意UE中的线程安容器有限)。 ### 代码示例 下面是一个简单的示例,展示如何在C++中定义一个使用TMap<FString, AActor*>作为参数的函数,并暴露给蓝图: ```cpp UCLASS() class MYGAME_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category = "MyCategory") static void FilterActorsByName(const TMap<FString, AActor*>& ActorMap, const FString& Filter, TArray<AActor*>& OutActors) { OutActors.Empty(); for (const auto& Pair : ActorMap) { if (Pair.Key.Contains(Filter)) { OutActors.Add(Pair.Value); } } } }; ``` 在蓝图中,我们可以这样调用: 1. 创建一个TMap变量,键为FString,值为AActor*(在蓝图中显示为对象引用,选择Actor类型)。 2. 调用`FilterActorsByName`函数,传入这个Map和一个字符串,得到匹配的Actor数组。 注意:在蓝图中填充TMap时,需要逐个添加键值对。 ### 总结 当函数参数中存在TMap<FString, AActor*>时,需要注意传递方式(避免复制)、内存管理(指针失效问题)、蓝图支持(类型转换和反射处理)以及迭代顺序等。通过合理设计函数接口和注意上述问题,可以有效地在UE4/UE5中使用TMap作为函数参数。 §§相关问题§§ 1. 如何在蓝图中创建并填充TMap<FString, AActor*>变量? 2. TMapTArray在处理关联数据时有何优缺点? 3. 在UE4/UE5中,如何安地在多线程环境下使用TMap4. 如何将TMap<FString, AActor*>序列化保存到磁盘? </think> ### 调用函数时使用 `TMap<FString, AActor*>` 的相关情况 #### 一、基础用法 1. **声明示例**: ```cpp void ProcessActorMap(const TMap<FString, AActor*>& ActorMap); ``` - 推荐使用 `const&` 传递以避免拷贝开销(引用[2]提到TMap内存非连续) - 键类型 `FString` 支持动态字符串,值类型 `AActor*` 需确保指向有效对象 2. **蓝图交互**: - 若需暴露给蓝图,需用 `UPROPERTY` 标记(引用[1]): ```cpp UPROPERTY(BlueprintReadWrite) TMap<FString, AActor*> BlueprintActorMap; ``` - 蓝图调用C++函数时,引擎自动处理 `TMap` 的反射序列化(引用[1]) #### 二、关键注意事项 1. **内存管理**: - **指针有效性**:`AActor*` 可能被GC回收,建议: - 使用 `TWeakObjectPtr<AActor>` 替代裸指针 - 或手动调用 `AddToRoot()` 防GC - **内存碎片**:频繁增删元素会导致稀疏数组(引用[2]),定期调用 `Compact()` 优化 2. **线程安**: ```cpp FRWLock MapLock; { FRWScopeLock ScopeLock(MapLock, SLT_Write); // 写锁 ActorMap.Add("Key", NewActor); } ``` - TMap 非线程安,多线程访问需同步 3. **迭代稳定性**: - 迭代期间增删元素会触发重新哈希(引用[2]),导致: - 迭代器失效 - 指针地址可能变化 - **解决方案**:临时复制Key数组再遍历 ```cpp TArray<FString> Keys; ActorMap.GenerateKeyArray(Keys); for (const FString& Key : Keys) {...} ``` #### 三、常见问题解决方案 1. **蓝图参数传递失败**: - **原因**:AActor* 需转换为 `UObject*` 反射(引用[1]) - **修复**:使用 `TMap<FString, UObject*>` 并验证类型 2. **序列化问题**: - **自定义序列化**: ```cpp void Serialize(FArchive& Ar) { Ar << ActorMap; // 需保证AActor已实现序列化 } ``` - 保存时需处理指针持久化(如转换为Path) 3. **性能优化**: - 预分配内存:`ActorMap.Empty(100);` - 避免频繁查找:对Key使用 `TNameToString` 常量 #### 四、最佳实践 1. **生命周期管理**: ```cpp virtual void EndPlay() override { ActorMap.Empty(); // 显式清空防野指针 } ``` 2. **蓝图友好封装**: ```cpp UFUNCTION(BlueprintCallable) void AddActorToMap(FString Key, AActor* Actor) { ActorMap.Add(Key, Actor); } ``` 3. **键冲突处理**: ```cpp AActor** Existing = ActorMap.Find(Key); if (Existing) *Existing = NewActor; // 替换 else ActorMap.Add(Key, NewActor); // 新增 ``` > 关键提示:当在蓝图C++间传递 `TMap` 时,引擎会通过 `TTuple` 自动包装参数(引用[1]),但需确保键值类型在反射系统中注册。涉及Actor指针时,必须考虑UObject的垃圾回收机制,避免野指针。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值