icon안동민 개발노트

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 컨테이너를 사용하면 코드의 효율성과 가독성을 높일 수 있지만, 언리얼 엔진의 기능을 최대한 활용하기 위해서는 엔진의 자체 컨테이너 클래스를 우선적으로 고려하는 것이 좋습니다.