[ UE5 ] GetClass vs StaticClass
코드를 짜다보면, 특정 객체의 클래스 정보를 알기 위해 GetClass 혹은 StaticClass 를 쓰는 것을 볼 수 있다.
그런데 이 둘의 차이는 무엇일까?
한 줄 요악 : StaticClass 는 컴파일 타임에서 UClass 타입의 정보를 얻어오는 것이며,
GetClass 는 런타임에서 실제 객체의 클래스를 조회할때 사용된다.
AMyActor* ActorPtr = NewObject<AMyActor>(...);
UObject* ObjPtr = Actor;
UClass* MyActorClass = AMyActor::StaticClass(); // AMyActor
UClass* ObjectClass = UObject::StaticClass(); // UObject
UClass* ActorPtrClass = ActorPtr->GetClass(); // AMyActor
UClass* ObjPtrClass = ObjPtr->GetClass(); // AMyActor
StaticClass 는 컴파일 타임에 이미 정해진 것이다. 하지만 포인터가 가리키는 실제 객체의 타입은 런타임에서 달라질 수 있으므로, 실제 객체의 클래스를 리턴하는 GetClass 의 리턴값은 StaticClass 의 리턴값과 다를 수 있다.
그렇다면 UClass 타입의 정보는 무엇이고 실제 객체의 정보는 무엇일까? 이를 이해하기 위해서는, UClass 타입과 CDO를 공부해야 한다.
CDO 는 무엇이고, 또 왜 사용할까
언리얼에서는 런타임에 빠른 타입 체킹과 클래스 검색을 위해, 컴파일 타임에서 클래스와 타입 등의 메타 정보를 생성한다. 이러한 메타 정보는 UClass 라는 언리얼 클래스에 보관된다. UClass 에는 클래스의 계층 구조나 멤버 변수/함수에 대한 정보가 들어있다. 이러한 UClass 를 이용하면, 단순히 타입을 검색하는 것을 넘어 런타임에서 인스턴스의 멤버 변수 값을 변경하거나 멤버 함수를 호출할 수도 있다. (Java, C# 에서는 이런 기능을 리플렉션[Reflection] 이라고 부른다)
런타임 과정에서는 언리얼 오브젝트를 초기화해야 하는데,
이때 생성되는 인스턴스가 CDO(Class Default Object), 클래스 기본 객체이다.
언리얼은 CDO 를 미리 생성해두고 필요에 따라 복제한다.
만약 100명의 캐릭터를 생성한다고 했을때, 각각의 캐릭터를 따로 인스턴스화하지 않고,
CDO 를 이용해 기본 틀을 생성한 후에, 속성값(SkeletalMesh, HP, 등등) 만 변경해주면
시간과 리소스를 절약할 수 있을 것이다.
언리얼 오브젝트의 생성자는 인스턴스를 초기화해 CDO 객체를 만들게 되는데, 실제 게임에서는 사용할 일이 거의 없다. (주로 Init 이나 BeginPlay 가 이에 해당한다)
언리얼 오브젝트는 모듈별로 관리된다
void ASampleCodingPractise::GetClassVsStaticClass()
{
UTestUObject* TestUObject = NewObject<UTestUObject>(this,TEXT("TestUObject"));
UClass* TestUObjectClass = TestUObject->GetClass();
UClass* TestUObjectStaticClass = UTestUObject::StaticClass();
UClass* TestUObjectStaticClassTakenFromGetClass = TestUObject->GetClass()->StaticClass();
UE_LOG(LogCodePractise, Log, TEXT("TestUObject->GetClass(): %s"), *TestUObject->GetClass()->GetName());
UE_LOG(LogCodePractise, Log, TEXT("UTestUObject::StaticClass(): %s"), *UTestUObject::StaticClass()->GetName());
UE_LOG(LogCodePractise, Log, TEXT("TestUObject->GetClass()->StaticClass(): %s"), *TestUObject->GetClass()->StaticClass()->GetName());
bool Test1 = false;
if (TestUObject->IsA(TestUObject->GetClass()))
{
Test1 = true;
}
UE_LOG(LogCodePractise, Log, TEXT("TestUObject->IsA(TestUObject->GetClass() result is: %s"), Test1?TEXT("True") : TEXT("False"));
bool Test2 = false;
if (TestUObject->IsA(UTestUObject::StaticClass()))
{
Test2 = true;
}
UE_LOG(LogCodePractise, Log, TEXT("TestUObject->IsA(UTestUObject::StaticClass()) result is: %s"), Test2 ? TEXT("True") : TEXT("False"));
bool Test3 = false;
if (TestUObject->IsA(TestUObject->GetClass()->StaticClass()))
{
Test3 = true;
}
UE_LOG(LogCodePractise, Log, TEXT("TestUObject->GetClass()->StaticClass()) result is: %s"), Test3 ? TEXT("True") : TEXT("False"));
}
결과
Output:
LogCodePractise: TestUObject->GetClass(): TestUObject
LogCodePractise: UTestUObject::StaticClass(): TestUObject
LogCodePractise: TestUObject->GetClass()->StaticClass(): Class
LogCodePractise: TestUObject->IsA(TestUObject->GetClass() result is: True
LogCodePractise: TestUObject->IsA(UTestUObject::StaticClass()) result is: True
LogCodePractise: TestUObject->GetClass()->StaticClass()) result is: False