GetClass() vs StaticClass()

Hey,

Decided to dig a little deeper in understanding UProperty’s and UClass’s etc. With this code I’m getting behavior I really don’t understand and I’m wondering if someone could explain this to me:

AUObjectTester::AUObjectTester(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	TestUObject = ObjectInitializer.CreateDefaultSubobject<UTestUObject>(this, TEXT("MyTestUObject"));

	UClass* TestUObjectClass = TestUObject->GetClass();
	UClass* TestUObjectStaticClass = UTestUObject::StaticClass();
	UClass* TestUObjectStaticClassTakenFromGetClass = TestUObject->GetClass()->StaticClass();

	PrimaryActorTick.bCanEverTick = true;
}

Here:

TestUObjectClass is name TestUObject.

TestUObjectStaticClass is name TestUObject

TestUObjectStaticClassTakenFromGetClass is name Class

but here in a function that is called 3 seconds after BeginPlay:

void AUObjectTester::TimedFunction()
{
	UClass* TestUObjectClass = TestUObject->GetClass();
	UClass* TestUObjectStaticClass = UTestUObject::StaticClass();
	UClass* TestUObjectStaticClassTakenFromGetClass = TestUObject->GetClass()->StaticClass();

	bool Test1 = false;
	if (TestUObject->IsA(TestUObject->GetClass()))
	{
		Test1 = true;
	}

	bool Test2 = false;
	if (TestUObject->IsA(UTestUObject::StaticClass()))
	{
		Test2 = true;
	}

	bool Test3 = false;
	if (TestUObject->IsA(TestUObject->GetClass()->StaticClass()))
	{
		Test3 = true;
	}
}

TestUObjectClass is name OnlineSession.

TestUObjectStaticClass is name TestUObject

TestUObjectStaticClassTakenFromGetClass is name Class

Also only test1 one passes.

Is there maybe something wrong with my code?

Do i not understand some kind of difference between what StaticClass() returns and GetClass() returns?

I’ve done a lot of unreal coding and never experienced any problems with checking what a class type an object is with the IsA() method, the behavior here is quite worrying to me and makes me think I don’t understand what is going on.

Anyway very confused and would appreciate some feedback.

Okay I think the issue was I didn’t mark TestUObject as a UPROPERTY.
Still looking into this.

GetClass() gets UClass of object instance, regardless what varable pointer you use.

StaticClass() is a static function (which means it does not require object instance) it will return UClass of class you calling from, so for example if you call AActor::StaticClass() it will return UClass of AActor class. This was created because C++ does not let you use class identifiers as varable content and reason of that is because for CPU classes and structures don’t exist, compiler simply make native code to read memory with specific offset to read specific variables in structure of class object. Thats why reflection system is needed and UE4 does that by generateing objects that identify classes and properties in it.

As for 3rd option you doing, keep in mind UClass is also UObject so it got it’s own UClass object so if you gonna call GetClass on UClass from StaticClass() with will be UClass of UClass class :stuck_out_tongue:

So test 1 passes because GetClass() return UClass of actually object so using GetClass on IsA() on same object always be true. 2nd fails because you checking base class where object is diffrent class. and 3rd fail;s because you getting UClass of UClass and it definitly not TestUObject.

IsA() return true if object you calling on is class of UClass you input in function. I know i the past i posted anwser where i said it also react if object is also parent of inputed class, but i was wrong so sorry if you seen it. There different function for that IsChildOf() in UClass so you do this way GetClass()->IsChildOf(AActor::StaticClass()) will return true on every actor

7 Likes

Thank you Shodowriver for you’re comprehensive and detailed response.
Though I just want to remark I am very familiar with c++ and understand the reason for the property system in UE4.

Concerning the 3rd test, I was stumped there but it’s completely logical, I feel kind of stupid for not seeing that.

The second test actually should have worked too because they were the same class, as in one was not the parent or child of the other.

What seemed to fix the problem was to mark my declaration of TestUObject with UPROPERTY() like so:

UPROPERTY()
UObject* TestUObject;

Which I should have done anyway because from what I understand not marking these UObject pointers with UPROPERTY() can prevent garbage collection from nulling the uobjects when they are destroyed, which can be quite dangerous.

It definitively seems dangerous in regards to the GetClass() method.
Without the UPROPERTY() marker, the GetClass() was returning gibberish.

By declaring TestUObject to be of type UObject, GetClass() considered the object to be of type UOnlineSession. Maybe not marking it was producing c++ code associating the variable with something coming before in the compile process. idk.

By declaring TestUObject to be of type UTestUObject, GetClass() returned something unreadable, seemingly random data.

What i found very peculiar was GetClass() was returning the correct UClass in the constructor but not in the run time code(BeginPlay etc), I think maybe this has something to do with with how unreal object system is set up regarding CDO’s etc, but I don’t know.

What confused me also was I thought GetClass() would work without marking a declaration with UPROPERTY(), I thought you could be able to dynamically spawn uobjects and be able to use GetClass() (I’m thinking in terms of an array of uobjects). Now I am not so sure anymore and will keep an eye out for this.

btw just as a side comment, i did also try calling GetClass() from inside the uobject and had the same results. I just wanted to be sure it’s not a matter of how one object is able to see another.

Please allow me to put my test code and the console output as below

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