STL 컨테이너 (vector, map)
C++ 표준 템플릿 라이브러리(STL)의 컨테이너는 데이터를 효율적으로 저장하고 관리하는 도구입니다.
이 절에서는 STL의 주요 컨테이너인 vector와 map을 살펴보고 이들을 언리얼 엔진에서 어떻게 활용할 수 있는지 알아보겠습니다.
vector
vector는 동적 배열을 구현한 컨테이너로, 연속된 메모리 공간에 요소를 저장합니다.
기본 사용법
##include <vector>
std::vector<int> scores;
scores.push_back(100);
scores.push_back(85);
int firstScore = scores[0]; // 100
언리얼 엔진에서의 활용 예시
UCLASS()
class AInventoryManager : public AActor
{
GENERATED_BODY()
private:
std::vector<AItem*> Items;
public:
UFUNCTION(BlueprintCallable, Category="Inventory")
void AddItem(AItem* Item)
{
Items.push_back(Item);
}
UFUNCTION(BlueprintCallable, Category="Inventory")
AItem* GetItem(int32 Index)
{
if (Index >= 0 && Index < Items.size())
{
return Items[Index];
}
return nullptr;
}
};
map
map은 키-값 쌍을 저장하는 연관 컨테이너로, 키를 기반으로 빠른 검색이 가능합니다.
기본 사용법
##include <map>
##include <string>
std::map<std::string, int> playerScores;
playerScores["Alice"] = 100;
playerScores["Bob"] = 85;
int aliceScore = playerScores["Alice"]; // 100
언리얼 엔진에서의 활용 예시
UCLASS()
class APlayerManager : public AActor
{
GENERATED_BODY()
private:
std::map<FString, APlayerCharacter*> Players;
public:
UFUNCTION(BlueprintCallable, Category="Players")
void RegisterPlayer(const FString& PlayerName, APlayerCharacter* PlayerCharacter)
{
Players[PlayerName] = PlayerCharacter;
}
UFUNCTION(BlueprintCallable, Category="Players")
APlayerCharacter* GetPlayer(const FString& PlayerName)
{
auto it = Players.find(PlayerName);
if (it != Players.end())
{
return it->second;
}
return nullptr;
}
};
언리얼 엔진의 컨테이너 클래스
언리얼 엔진은 STL과 유사한 기능을 제공하는 자체 컨테이너 클래스를 가지고 있습니다.
TArray (STL의 vector와 유사)
UPROPERTY()
TArray<AItem*> Items;
Items.Add(NewItem);
AItem* FirstItem = Items[0];
TMap (STL의 map과 유사)
UPROPERTY()
TMap<FString, APlayerCharacter*> Players;
Players.Add("Alice", AliceCharacter);
APlayerCharacter* AliceChar = Players["Alice"];
STL 컨테이너와 언리얼 컨테이너의 비교
1. 성능
- STL 컨테이너 : 일반적으로 빠른 성능을 제공합니다.
- 언리얼 컨테이너 : 게임 개발에 최적화되어 있으며, 특히 언리얼 엔진의 메모리 관리 시스템과 잘 통합됩니다.
2. 메모리 관리
- STL 컨테이너 : 표준 C++ 할당자를 사용합니다.
- 언리얼 컨테이너 : 언리얼 엔진의 메모리 할당자를 사용하여 게임 개발에 최적화된 메모리 관리를 제공합니다.
3. 직렬화 및 리플렉션
- STL 컨테이너 : 언리얼 엔진의 직렬화 시스템과 호환되지 않습니다.
- 언리얼 컨테이너 : UPROPERTY() 매크로와 함께 사용하여 자동 직렬화 및 리플렉션을 지원합니다.
4. 블루프린트 호환성
- STL 컨테이너 : 블루프린트에서 직접 사용할 수 없습니다.
- 언리얼 컨테이너 : 블루프린트와 완벽하게 호환됩니다.
적절한 사용 상황
1. STL 컨테이너
- 순수 C++ 로직에서 사용할 때
- 서드파티 C++ 라이브러리와 인터페이스할 때
- 성능이 매우 중요한 저수준 시스템에서
2. 언리얼 컨테이너
- 게임플레이 로직에서 사용할 때
- 블루프린트와 상호작용이 필요할 때
- 언리얼 엔진의 직렬화 및 리플렉션 시스템을 활용해야 할 때
성능 고려사항 및 메모리 관리
1. 벡터 크기 예약
std::vector<int> scores;
scores.reserve(100); // 100개의 요소를 위한 메모리를 미리 할당
2. 불필요한 복사 피하기
// 비효율적
for (auto item : items) { ... }
// 효율적
for (const auto& item : items) { ... }
3. 삽입 및 삭제 연산 최소화
- vector의 중간에 삽입하거나 삭제하는 것은 비용이 많이 듭니다.
- map의 경우 삽입과 삭제가 vector보다 효율적이지만, 여전히 주의가 필요합니다.
4. 가비지 컬렉션 고려
- 언리얼 객체(UObject 파생 클래스)를 STL 컨테이너에 저장할 때는 가비지 컬렉션 문제에 주의해야 합니다.
- 가능하면 언리얼 컨테이너를 사용하거나, 약한 포인터(TWeakObjectPtr)를 사용하세요.
효과적인 사용을 위한 팁
1. 컨테이너 선택 시 고려사항
- 데이터의 크기와 접근 패턴
- 삽입/삭제 빈도
- 메모리 사용량
- 성능 요구사항
2. 이터레이터 사용
for (auto it = myMap.begin(); it != myMap.end(); ++it)
{
// it->first는 키, it->second는 값
}
3. 알고리즘 활용
##include <algorithm>
std::vector<int> numbers = {5, 2, 8, 1, 9};
std::sort(numbers.begin(), numbers.end());
4. 커스텀 비교자 사용
struct CompareByScore
{
bool operator()(const APlayer* a, const APlayer* b) const
{
return a->GetScore() < b->GetScore();
}
};
std::map<APlayer*, int, CompareByScore> playerScores;
5. 언리얼 엔진과의 통합
- STL 컨테이너를 사용하는 경우 필요에 따라 언리얼 컨테이너로 변환하는 헬퍼 함수를 만들어 사용하세요.
TArray<FString> ConvertToTArray(const std::vector<std::string>& stdVector)
{
TArray<FString> result;
for (const auto& item : stdVector)
{
result.Add(FString(item.c_str()));
}
return result;
}
STL 컨테이너는 강력하고 유연한 도구이지만, 언리얼 엔진에서 사용할 때는 엔진의 특성과 요구사항을 고려해야 합니다.
적절한 상황에서 STL 컨테이너를 사용하면 코드의 효율성과 가독성을 높일 수 있지만, 언리얼 엔진의 기능을 최대한 활용하기 위해서는 엔진의 자체 컨테이너 클래스를 우선적으로 고려하는 것이 좋습니다.