UE5 C++ 컨테이너 완전 가이드
개요 — 왜 STL 대신 UE 컨테이너를 쓰는가
Section titled “개요 — 왜 STL 대신 UE 컨테이너를 쓰는가”언리얼 엔진은 std::vector, std::unordered_map, std::unordered_set 대신 자체 컨테이너를 제공합니다. UE 컨테이너는 다음 장점을 가집니다.
| 항목 | STL | UE 컨테이너 |
|---|---|---|
| 메모리 할당자 | OS 기본 | FMemory (커스텀 추적 가능) |
| UPROPERTY 직렬화 | 불가 | 가능 (TArray, TMap, TSet) |
| Blueprint 노출 | 불가 | 가능 |
| 멀티플랫폼 일관성 | 구현 의존 | 엔진 보장 |
1. TArray
Section titled “1. TArray”TArray<T>는 동적 크기 배열로, C++ std::vector에 대응합니다. 메모리가 연속적으로 배치되어 캐시 친화적입니다.
1.1 기본 사용법
Section titled “1.1 기본 사용법”#include "Containers/Array.h"
TArray<int32> Scores;
// 추가Scores.Add(100);Scores.Add(85);Scores.Emplace(72); // Add보다 효율적 (이동 생성자 활용)
// 예약 (재할당 방지)TArray<FVector> Positions;Positions.Reserve(1000);
// 삽입 / 제거Scores.Insert(90, 0); // 인덱스 0에 삽입Scores.RemoveAt(1); // 인덱스 1 제거 (순서 유지, O(n))Scores.RemoveAtSwap(0); // 스왑 후 제거 (순서 변경, O(1))
// 크기 및 초기화int32 Count = Scores.Num();Scores.SetNum(5); // 크기 조정 (0으로 채움)Scores.Init(0, 10); // 10개 요소를 0으로 초기화1.2 검색
Section titled “1.2 검색”TArray<FString> Names = { TEXT("Alice"), TEXT("Bob"), TEXT("Carol") };
// 선형 탐색int32 Idx = Names.IndexOfByKey(TEXT("Bob")); // 없으면 INDEX_NONEbool bFound = Names.Contains(TEXT("Carol"));
// 조건 검색FString* Found = Names.FindByPredicate([](const FString& Name){ return Name.StartsWith(TEXT("A"));});
// 필터링TArray<FString> Filtered = Names.FilterByPredicate([](const FString& Name){ return Name.Len() > 3;});1.3 정렬
Section titled “1.3 정렬”TArray<int32> Nums = { 5, 2, 8, 1, 9 };
// 기본 오름차순 정렬Nums.Sort();
// 람다 커스텀 정렬Nums.Sort([](const int32 A, const int32 B) { return A > B; }); // 내림차순
// 안정 정렬 (동일 요소의 상대 순서 유지)Nums.StableSort();
// UObject 배열 정렬 예시TArray<TObjectPtr<AActor>> Actors;Actors.Sort([](const TObjectPtr<AActor>& A, const TObjectPtr<AActor>& B){ return A->GetActorLocation().Z < B->GetActorLocation().Z;});1.4 반복
Section titled “1.4 반복”TArray<FString> Tags = { TEXT("Enemy"), TEXT("Boss"), TEXT("Flying") };
// 범위 기반 forfor (const FString& Tag : Tags){ UE_LOG(LogTemp, Log, TEXT("Tag: %s"), *Tag);}
// 인덱스 기반for (int32 i = 0; i < Tags.Num(); ++i){ UE_LOG(LogTemp, Log, TEXT("[%d] %s"), i, *Tags[i]);}
// 역방향 순회 (제거 시 안전)for (int32 i = Tags.Num() - 1; i >= 0; --i){ if (Tags[i].Contains(TEXT("Boss"))) { Tags.RemoveAt(i); }}1.5 UPROPERTY 선언
Section titled “1.5 UPROPERTY 선언”UCLASS()class MYGAME_API AEnemySpawner : public AActor{ GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, Category = "Spawn") TArray<TSubclassOf<AEnemy>> EnemyClasses;
UPROPERTY(VisibleAnywhere, Category = "State") TArray<TObjectPtr<AEnemy>> ActiveEnemies;};2. TMap
Section titled “2. TMap”TMap<K, V>는 키-값 쌍을 저장하는 해시 테이블입니다. std::unordered_map에 대응하며, 삽입·조회가 평균 O(1)입니다.
2.1 기본 사용법
Section titled “2.1 기본 사용법”TMap<FString, int32> ItemCounts;
// 추가ItemCounts.Add(TEXT("Sword"), 3);ItemCounts.Add(TEXT("Shield"), 1);ItemCounts.Emplace(TEXT("Potion"), 10);
// 조회int32* CountPtr = ItemCounts.Find(TEXT("Sword")); // 없으면 nullptrint32 Count = ItemCounts.FindOrAdd(TEXT("Arrow")); // 없으면 기본값 추가
// 존재 확인bool bHasShield = ItemCounts.Contains(TEXT("Shield"));
// 제거ItemCounts.Remove(TEXT("Shield"));
// 크기int32 Size = ItemCounts.Num();2.2 반복
Section titled “2.2 반복”TMap<FName, float> AttributeMap;AttributeMap.Add(TEXT("Strength"), 10.f);AttributeMap.Add(TEXT("Agility"), 8.f);AttributeMap.Add(TEXT("Intelligence"), 12.f);
// 키-값 쌍 순회for (const TPair<FName, float>& Pair : AttributeMap){ UE_LOG(LogTemp, Log, TEXT("%s: %.1f"), *Pair.Key.ToString(), Pair.Value);}
// 키만 순회TArray<FName> Keys;AttributeMap.GetKeys(Keys);
// 값만 순회TArray<float> Values;AttributeMap.GenerateValueArray(Values);2.3 TMultiMap — 중복 키 허용
Section titled “2.3 TMultiMap — 중복 키 허용”// 하나의 키에 여러 값이 필요할 때TMultiMap<FString, FString> TagMap;TagMap.Add(TEXT("Enemy"), TEXT("Ground"));TagMap.Add(TEXT("Enemy"), TEXT("Flying"));TagMap.Add(TEXT("NPC"), TEXT("Merchant"));
TArray<FString> EnemyTypes;TagMap.MultiFind(TEXT("Enemy"), EnemyTypes);// EnemyTypes = { "Flying", "Ground" }3. TSet
Section titled “3. TSet”TSet<T>는 고유 요소만 저장하는 해시 집합입니다. std::unordered_set에 대응하며, 중복 제거와 빠른 포함 여부 확인에 최적입니다.
3.1 기본 사용법
Section titled “3.1 기본 사용법”TSet<FName> UnlockedAbilities;
// 추가 (중복 무시됨)UnlockedAbilities.Add(TEXT("Dash"));UnlockedAbilities.Add(TEXT("Dash")); // 두 번 추가해도 1개만 저장UnlockedAbilities.Add(TEXT("Fireball"));
// 포함 확인 (O(1))bool bHasDash = UnlockedAbilities.Contains(TEXT("Dash"));
// 제거UnlockedAbilities.Remove(TEXT("Fireball"));
int32 Size = UnlockedAbilities.Num();3.2 집합 연산
Section titled “3.2 집합 연산”TSet<int32> SetA = { 1, 2, 3, 4 };TSet<int32> SetB = { 3, 4, 5, 6 };
// 교집합TSet<int32> Intersection = SetA.Intersect(SetB); // { 3, 4 }
// 합집합TSet<int32> Union = SetA.Union(SetB); // { 1, 2, 3, 4, 5, 6 }
// 차집합TSet<int32> Difference = SetA.Difference(SetB); // { 1, 2 }4. 컨테이너 선택 기준
Section titled “4. 컨테이너 선택 기준”| 요구사항 | 컨테이너 | 이유 |
|---|---|---|
| 순서 있는 목록, 랜덤 인덱스 접근 | TArray | 연속 메모리, O(1) 인덱스 |
| 키로 빠른 값 조회 | TMap | 해시 O(1) 평균 조회 |
| 중복 없는 집합, 빠른 존재 확인 | TSet | 해시 O(1) 포함 확인 |
| 하나의 키에 여러 값 | TMultiMap | 중복 키 허용 |
5. 실전 예시 — 인벤토리 시스템
Section titled “5. 실전 예시 — 인벤토리 시스템”UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))class MYGAME_API UInventoryComponent : public UActorComponent{ GENERATED_BODY()
public: UFUNCTION(BlueprintCallable, Category = "Inventory") void AddItem(FName ItemID, int32 Quantity = 1);
UFUNCTION(BlueprintCallable, Category = "Inventory") bool RemoveItem(FName ItemID, int32 Quantity = 1);
UFUNCTION(BlueprintPure, Category = "Inventory") int32 GetItemCount(FName ItemID) const;
UFUNCTION(BlueprintPure, Category = "Inventory") bool HasItem(FName ItemID) const;
private: // 아이템 ID → 수량 (빠른 조회) UPROPERTY(VisibleAnywhere, Category = "Inventory") TMap<FName, int32> ItemInventory;
// 장착 중인 아이템 (중복 불필요) UPROPERTY(VisibleAnywhere, Category = "Inventory") TSet<FName> EquippedItems;
// 획득 이력 (순서 유지) UPROPERTY(VisibleAnywhere, Category = "Inventory") TArray<FName> AcquisitionLog;};void UInventoryComponent::AddItem(FName ItemID, int32 Quantity){ int32& Count = ItemInventory.FindOrAdd(ItemID); Count += Quantity; AcquisitionLog.AddUnique(ItemID); // 중복 없이 이력 추가}
bool UInventoryComponent::RemoveItem(FName ItemID, int32 Quantity){ int32* CountPtr = ItemInventory.Find(ItemID); if (!CountPtr || *CountPtr < Quantity) { return false; }
*CountPtr -= Quantity; if (*CountPtr <= 0) { ItemInventory.Remove(ItemID); EquippedItems.Remove(ItemID); // 장착 해제 } return true;}
int32 UInventoryComponent::GetItemCount(FName ItemID) const{ const int32* CountPtr = ItemInventory.Find(ItemID); return CountPtr ? *CountPtr : 0;}
bool UInventoryComponent::HasItem(FName ItemID) const{ return ItemInventory.Contains(ItemID);}- TArray: 순서가 중요하거나 인덱스로 접근이 잦을 때 — 범용 컨테이너 1순위
- TMap: 키로 즉시 값을 찾아야 할 때 — O(1) 조회
- TSet: 중복 없이 요소를 관리하고 빠른 포함 확인이 필요할 때
Reserve()로 예약하면 대량 추가 시 재할당을 방지해 성능이 향상됩니다.- UPROPERTY로 선언하면 에디터 노출 및 직렬화가 자동으로 지원됩니다.