이득우C++_07_컨테이너 라이브러리
TArray, TSet의 특징에 대한 문서
1. TArray와 TSet 비교
TArray는 동적배열 -> Vector와 유사
TSet은 Unordered Set을 생각하면 된다.
- 중복허용 안함
- 헤시테이블 형태로 되어있음
- TSet의 경우 처음 채워질 때는 순서대로 채워지지만 원소 삭제 후 다시 Add를 하면 순서대로 채워지지 않고 정렬도 되지 않는다.
- 검색의 용도로 사용하면 좋다.
2. TArray 코드
#include "MyGameInstance.h"
#include "Algo/Accumulate.h" //알고리즘 헤더
void UMyGameInstance::Init()
{
Super::Init();
const int32 ArrayNum = 10;
TArray<int32> Int32Array;
Int32Array.SetNum(10); //원소 공간확보 후 기본값으로 초기화
for (int32 ix = 1; ix <= ArrayNum; ++ix)
Int32Array.Add(ix); // 원소 추가
Int32Array.RemoveAll([](int32 Val) // 람다를 이용해서 조건에 맞는 원소 전부 삭제 가능
{
return Val % 2 == 0;
});
Int32Array += {2, 4, 6, 8, 10}; // 중괄호로 단체로 값 추가 가능
TArray<int32> Int32ArrayCompare;
int32 CArray[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
Int32ArrayCompare.AddUninitialized(ArrayNum); // 초기화 없이 공간확보
FMemory::Memcpy(Int32ArrayCompare.GetData(), CArray, sizeof(int32) * ArrayNum); // Memcpy 사용법
ensure(Int32Array == Int32ArrayCompare); // 조건만족하지 않으면 로그 상에 에러문구 띄움
//check(Int32Array == Int32ArrayCompare); // 조건만족하지 않으면 프로그램 중지시킴
int32 Sum = 0;
for (const int32& Int32Elem : Int32Array) //범위지정 for문 사용가능
Sum += Int32Elem;
ensure(Sum == 55);
int32 SumByAlgo = Algo::Accumulate(Int32Array, 0); //Int32Array 의 Index 0번 부터의 모든 원소 합계
ensure(SumByAlgo == 55);
}
3. TSet 코드
TSet<int32> Int32Set;
for (int32 ix = 1; ix <= ArrayNum; ++ix)
Int32Set.Add(ix);
Int32Set.Remove(2); //Set은 TArray처럼 일괄적으로 지우는 함수가 없음
Int32Set.Remove(4);
Int32Set.Remove(6);
Int32Set.Remove(8);
Int32Set.Remove(10); //1 3 5 7 9
Int32Set.Add(2);
Int32Set.Add(4);
Int32Set.Add(6);
Int32Set.Add(8);
Int32Set.Add(10); //1 10 3 8 5 6 7 4 9 2
4. UStruct
특성 | 구조 |
---|---|
4-1. 선언
USTRUCT()
struct FStudentData
{
GENERATED_BODY()
FStudentData()
{
Name = TEXT("홍길동");
Order = -1;
}
//UObject와 다르게 인자있는 생성자를 만들 수 있다.
FStudentData(FString InName, int32 InOrder) : Name(InName), Order(InOrder) {}
...
UPROPERTY()
FString Name;
UPROPERTY()
int32 Order;
};
4-2. 구조체 사용
private:
TArray<FStudentData> StudentsData; //값타입이라서 리플렉션이 필요없음
UPROPERTY() //UObject의 포인터를 맴버변수로 갖는경우 무조건 UPROPERTY() 필수
TArray<TObjectPtr<class UStudent>> Students;
UStruct는 값형식으로 메모리관리가 필요없기 때문에 리플렉션 선언이 없어도 됨
4-3. 구조체 TArray로 채우기
FString MakeRandomName()
{
TCHAR FirstChar[] = TEXT("김이박최");
TCHAR MiddleChar[] = TEXT("상혜지성");
TCHAR LastChar[] = TEXT("수은원연");
TArray<TCHAR> RandArray;
RandArray.SetNum(3);
RandArray[0] = FirstChar[FMath::RandRange(0, 3)]; //범위 안에서 랜덤 숫자 뽑는 함수
RandArray[1] = MiddleChar[FMath::RandRange(0, 3)];
RandArray[2] = LastChar[FMath::RandRange(0, 3)];
return RandArray.GetData();
}
...
const int32 StudentNum = 300;
for (int32 ix = 1; ix <= StudentNum; ++ix)
{
StudentsData.Emplace(FStudentData(MakeRandomName(), ix));
}
TArray<FString> AllStudentsNames;
Algo::Transform(StudentsData, AllStudentsNames, //Algo::Transform -> 1번 컨테이너를 2번 컨테이너로 복사하는 함수. 복사하는 값은 람다 이용
[](const FStudentData& Val)
{
return Val.Name;
}
);
//LogTemp: 모든 학생 이름의 수 : 300
UE_LOG(LogTemp, Log, TEXT("모든 학생 이름의 수 : %d"), AllStudentsNames.Num());
4-4. TSet에 중복검사
TSet<FString> AllUniqueNames;
Algo::Transform(StudentsData, AllUniqueNames,
[](const FStudentData& Val)
{
return Val.Name;
}
);
//LogTemp: 중복 없는 학생 이름의 수 : 63
UE_LOG(LogTemp, Log, TEXT("중복 없는 학생 이름의 수 : %d"), AllUniqueNames.Num());
4-5. TSet이나 TMap에 구조체 사용
struct FStudentData
{
...
//Set과 Map에서 쓰기 위해서 Operator==추가 Hash 함수 추가
bool operator==(const FStudentData& InOther) const
{
return Order == InOther.Order;
}
//Set과 Map에서 쓰기 위해서 GetTypeHash 함수 추가
friend FORCEINLINE uint32 GetTypeHash(const FStudentData& InStudentData)
{
return GetTypeHash(InStudentData.Order);
}
...
};
...
TSet<FStudentData> StudentsSet;
for (int32 ix = 1; ix <= StudentNum; ++ix)
{
StudentsSet.Emplace(FStudentData(MakeRandomName(), ix));
}
Hash를 사용하는 컨테이너에 사용자 정의 구조체를 넣기 위해서는 operator== 와 GetTypeHash() 함수 구현필요
5. TMap
특징
- Key와 Value가 쌍으로 pair를 이루는 언리얼 컨테이너
- STL unordered_map 과 유사
- TSet과 동일하게 key가 Hash로 되어있어서 사용자 정의 구조체를 추가할 경우 GetTypeHash() 구현필요
- TMap의 경우 key 중복 불가
- TMultiMap의 경우 Key 중복허용
5-1. TMap 사용
TMap<int32, FString> StudentsMap;
...
Algo::Transform(StudentsData, StudentsMap,
[](const FStudentData& Val)
{
return TPair<int32, FString>(Val.Order, Val.Name);
}
);
// LogTemp: 순번에 따른 학생 맵의 레코드 수 : 300
UE_LOG(LogTemp, Log, TEXT("순번에 따른 학생 맵의 레코드 수 : %d"), StudentsMap.Num());
TMap<FString, int32> StudentsMapByUniqueName;
Algo::Transform(StudentsData, StudentsMapByUniqueName,
[](const FStudentData& Val)
{
return TPair<FString, int32>(Val.Name, Val.Order);
}
);
// LogTemp: 이름에 따른 학생 맵의 레코드 수 : 63
UE_LOG(LogTemp, Log, TEXT("이름에 따른 학생 맵의 레코드 수 : %d"), StudentsMapByUniqueName.Num());
5-2. TMultiMap 사용
TMultiMap<FString, int32> StudentMapByName;
Algo::Transform(StudentsData, StudentMapByName,
[](const FStudentData& Val)
{
return TPair<FString, int32>(Val.Name, Val.Order);
}
);
// LogTemp: 이름에 따른 학생 멀티맵의 레코드 수 : 300
UE_LOG(LogTemp, Log, TEXT("이름에 따른 학생 멀티맵의 레코드 수 : %d"), StudentMapByName.Num());
const FString TargetName(TEXT("이혜은"));
TArray<int32> AllOrders;
StudentMapByName.MultiFind(TargetName, AllOrders); //MultiFind로 조건에 맞는 TArray를 뽑아올 수 있다.
// LogTemp: 이름이 이혜은인 학생 수 : 3
UE_LOG(LogTemp, Log, TEXT("이름이 %s인 학생 수 : %d"), *TargetName, AllOrders.Num());
댓글남기기