TUniqueFunction

TUniqueFunction()

在虚幻引擎(Unreal Engine)中,TUniqueFunction 是一种模板类,用于封装可调用对象(如函数、lambda表达式、仿函数等),并保证这些可调用对象具有唯一所有权。这意味着 TUniqueFunction 类似于 std::unique_ptr,它确保所管理的对象只能由一个 TUniqueFunction 实例拥有。

TUniqueFunction 主要用来处理线程安全任务或延迟执行操作,它的特点是只允许一个实例拥有其封装的可调用对象,防止多个地方同时修改或调用同一个对象导致不安全的行为。它通常用于需要移动语义的场景,不允许拷贝操作。

以下是 TUniqueFunction 的一些关键特点和使用示例:

关键特点

  1. 唯一所有权:每个 TUniqueFunction 实例独占对其内部可调用对象的所有权,不支持复制,只支持移动。
  2. 类型安全:它是一个模板类,允许存储任何符合签名的可调用对象。
  3. 线程安全:适用于多线程环境下的延迟执行操作。

示例代码

#include "CoreMinimal.h"
#include <iostream>

// 一个简单的函数,符合 TUniqueFunction 的签名
void SimpleFunction()
{
    std::cout << "Hello, Unreal!" << std::endl;
}

void ExampleUsage()
{
    // 使用 lambda 表达式创建一个 TUniqueFunction
    TUniqueFunction<void()> UniqueFunc = []() {
        std::cout << "Lambda function called" << std::endl;
    };

    // 调用 TUniqueFunction
    UniqueFunc();

    // 将一个普通函数分配给 TUniqueFunction
    TUniqueFunction<void()> UniqueFunc2 = SimpleFunction;

    // 调用 TUniqueFunction
    UniqueFunc2();
    
    // TUniqueFunction 不能被拷贝,但可以被移动
    TUniqueFunction<void()> MovedFunc = MoveTemp(UniqueFunc2);

    // 调用移动后的 TUniqueFunction
    MovedFunc();
}

int main()
{
    ExampleUsage();
    return 0;
}

项目中的代码示例

TUniqueFunction<TArray<AActor*>(const FString&, EFurnitureType, bool, bool)> spawnFunc = [&](const FString& furnitureName, EFurnitureType furnitureType, bool bForceCreate, bool bCreateOne)
	{
		TArray<AActor*> furnitures;
		FString FurnitureName = furnitureName;
		if (FurnitureName.IsEmpty())
			FurnitureName = enumPtr->GetNameStringByIndex(furnitureType);
		if (!Context->FurnitureInfoMap.Contains(FurnitureName) && !FurnitureChildMap.Contains(FurnitureName))
			return furnitures;
		if (!Context->FurnitureMap.Contains(FurnitureName))
			Context->FurnitureMap.Add(FurnitureName, {});
		if (!bForceCreate && Context->FurnitureMap[FurnitureName].Num() > 0) {
			return Context->FurnitureMap[FurnitureName];
		}

		TUniqueFunction<TArray<AActor*>(const FString&, bool)> SpawnFurniture = [=](const FString& furnitureType, bool bList)
		{
			TArray<AActor*> actors;
			if (Context->FurnitureInfoMap.Contains(furnitureType)) {
				TArray<FRoomFurniture>& paramFurnitures = Context->FurnitureInfoMap[furnitureType];
				for (FRoomFurniture& furniture : paramFurnitures) {
					TArray<FName> tags;
					tags.Add(*("type:" + furniture.Type));
					tags.Add(*("roomName:" + roomName));
					if (furniture.Type.Contains("Door") || furniture.Type.Contains("Window")) {
						tags.Add("unSelect:true");
					}

					AActor* actor = nullptr;
					//#if WITH_EDITOR || PLATFORM_WINDOWS
					if (furniture.TestActor != nullptr) {
						actor = furniture.TestActor;
						furniture.TestClass = furniture.TestActor->GetClass();
						furniture.TestActor = nullptr;
					}
					if (actor == nullptr && furniture.TestClass != nullptr) {
						actor = World->SpawnActor<AActor>(furniture.TestClass);
						if (actor->IsA<AStaticMeshActor>()) {
							AStaticMeshActor* staticMeshActor = Cast<AStaticMeshActor>(actor);
							staticMeshActor->SetMobility(EComponentMobility::Movable);
						}
						actor->Tags = tags;
						MaskObject(actor, TEXT("soft"));
						AddTag(actor, "id", furniture.ID);
					}
					if (actor == nullptr) {
						actor = SpawnActor(furniture.ID, FVector(0, 0, 0), FRotator(0, 0, 0), "", tags);
					}
					if (actor != nullptr) {
						actors.Add(actor);
						Context->FurnitureTypeMap.Add(actor, furniture.Type);
						Context->FurnitureInfoMap[furnitureType][0].ActorList.Add(actor);
					}
					if (!bList)
						break;
				}
			}
			if (!actors.IsEmpty()) {
				if (!Context->FurnitureMap.Contains(furnitureType))
					Context->FurnitureMap.Add(furnitureType, {});
				Context->FurnitureMap[furnitureType].Append(actors);
			}
			return actors;
		};

		if (FurnitureChildMap.Contains(FurnitureName)) {
			for (const FString& type : FurnitureChildMap[FurnitureName]) {
				furnitures.Append(SpawnFurniture(type, !bCreateOne));
				if (!furnitures.IsEmpty() && !bCreateOne)
					break;
			}
		}
		else {
			furnitures.Append(SpawnFurniture(FurnitureName, !bCreateOne));
		}

		return furnitures;
	};
Context->SpawnFurnitures->SpawnFunc = &spawnFunc;

注意事项

  • TUniqueFunction 不能被复制,只能通过移动操作来转移所有权。这与 std::unique_ptr 类似。
  • 它适用于需要确保单一所有权的场景,例如异步任务或延迟执行的函数调用。
  • 在多线程环境中使用 TUniqueFunction 时,确保线程安全性是很重要的。

通过使用 TUniqueFunction,开发者可以更方便地管理可调用对象的生命周期和所有权,同时确保代码的安全性和简洁性。

posted @ 2024-06-14 11:09  Dream_moon  阅读(66)  评论(0)    收藏  举报