UObject & Reflection
Unreal's UObject base class, the reflection system, the UCLASS/UPROPERTY/UFUNCTION macros, and object lifecycle.
20 questions
JuniorTheoryVery commonWhat is a UObject and why is it central to Unreal Engine?
What is a UObject and why is it central to Unreal Engine?
UObject is the base class for nearly every managed Unreal type. It enables reflection, serialization, garbage collection, networking, and Blueprint exposure. Classes outside the UObject hierarchy get none of these engine services.
Common mistakes
- ✗Thinking plain C++ classes get reflection or GC without deriving from
UObject - ✗Confusing
UObjectengine services with standard C++ RTTI - ✗Assuming every
UObjectis an Actor placed in the world
Follow-up questions
- →How does the engine discover a
UObjectsubclass's properties at runtime? - →What does a class lose if it derives from a plain C++
classinstead?
JuniorTheoryVery commonWhat are the UPROPERTY() and UFUNCTION() macros used for?
What are the UPROPERTY() and UFUNCTION() macros used for?
UPROPERTY() exposes a member variable to the reflection system — enabling editor editing, serialization, replication, and GC tracking. UFUNCTION() does the same for a method, enabling Blueprint calls, RPCs, and delegate binding.
Common mistakes
- ✗Thinking serialization or GC tracking happens without
UPROPERTY() - ✗Believing
UPROPERTY()generates getters and setters - ✗Assuming the macros only affect the editor Details panel
Follow-up questions
- →Which
UPROPERTYspecifier controls editor editability? - →What happens to a non-
UPROPERTYUObject*during garbage collection?
JuniorTheoryCommonWhat is the difference between BlueprintCallable and BlueprintPure?
What is the difference between BlueprintCallable and BlueprintPure?
BlueprintCallable functions have an execution pin and may change state. BlueprintPure functions have no exec pin, are assumed side-effect-free, and re-run every time their output is read. Use Pure only for cheap, deterministic getters.
Common mistakes
- ✗Marking an expensive function
BlueprintPureand having it re-run repeatedly - ✗Giving a
BlueprintPurefunction side effects - ✗Thinking
BlueprintPureresults are cached for the frame
Follow-up questions
- →Why can a
BlueprintPurenode execute multiple times in one graph? - →When should a getter still be
BlueprintCallable?
JuniorTheoryCommonHow do UObject, AActor, UActorComponent, and USceneComponent relate?
How do UObject, AActor, UActorComponent, and USceneComponent relate?
AActor and UActorComponent both derive from UObject. An AActor can be placed in a level and owns components. UActorComponent is attached behaviour; USceneComponent is a UActorComponent that adds a transform, so it has a world position.
Common mistakes
- ✗Thinking a
UActorComponentcan exist in a level without an owning Actor - ✗Believing
USceneComponentis the base andUActorComponentthe specialised type - ✗Assuming every component has a world transform
Follow-up questions
- →Why is a
UActorComponentwithout a transform still useful? - →What does attaching one
USceneComponentto another establish?
JuniorTheoryCommonWhat do EditAnywhere, VisibleAnywhere, BlueprintReadOnly, and BlueprintReadWrite mean?
What do EditAnywhere, VisibleAnywhere, BlueprintReadOnly, and BlueprintReadWrite mean?
EditAnywhere lets a property be changed in the editor Details panel; VisibleAnywhere shows it read-only there. BlueprintReadWrite lets Blueprint graphs get and set it; BlueprintReadOnly allows only reading. Edit/Visible target the editor, Blueprint* target graphs.
Common mistakes
- ✗Thinking
VisibleAnywherehides the property instead of showing it read-only - ✗Confusing the editor specifiers with the Blueprint specifiers
- ✗Assuming these specifiers control replication
Follow-up questions
- →What is the difference between
EditAnywhereandEditDefaultsOnly? - →Which specifier controls replication instead?
MiddleTheoryCommonHow do the constructor, PostInitializeComponents, BeginPlay, and Tick differ?
How do the constructor, PostInitializeComponents, BeginPlay, and Tick differ?
The constructor sets defaults and creates components — it runs with no world. PostInitializeComponents runs once components are registered, before play. BeginPlay runs when gameplay starts. Tick runs every frame. Each marks a later, more complete stage.
Common mistakes
- ✗Assuming the constructor has access to a valid world
- ✗Putting per-frame logic in
BeginPlayinstead ofTick - ✗Not knowing
PostInitializeComponentsruns beforeBeginPlay
Follow-up questions
- →Why can a component reference be used safely in
PostInitializeComponentsbut not the constructor? - →How do you disable
Tickto save performance?
MiddleTheoryCommonWhat is the Class Default Object (CDO) in Unreal Engine?
What is the Class Default Object (CDO) in Unreal Engine?
The CDO is a single template instance the engine creates per UClass. It holds the default values for every property. New instances are initialized by copying the CDO, and the editor edits the CDO itself when you change a class's defaults.
Common mistakes
- ✗Thinking there is a CDO per instance rather than per class
- ✗Running gameplay logic in a constructor, which also executes on the CDO
- ✗Assuming the CDO is discarded at runtime
Follow-up questions
- →Why does the constructor run on the CDO at editor load time?
- →How does editing a class default propagate to existing instances?
MiddleTheoryCommonWhat happens if you forget UPROPERTY() on a UObject* reference?
What happens if you forget UPROPERTY() on a UObject* reference?
Without UPROPERTY() the garbage collector cannot see the reference, so it does not keep the object alive and does not null the pointer when the object is collected. The result is a dangling pointer that crashes later — a delayed, hard-to-trace bug.
Common mistakes
- ✗Expecting a compile error instead of a silent runtime bug
- ✗Thinking the GC scans raw pointers without
UPROPERTY() - ✗Assuming the symptom is a leak rather than a dangling pointer
Follow-up questions
- →Why does the crash often appear long after the mistake?
- →When is a raw
UObject*withoutUPROPERTY()actually acceptable?
MiddleTheoryCommonWhat do the UCLASS() and GENERATED_BODY() macros do?
What do the UCLASS() and GENERATED_BODY() macros do?
UCLASS() marks a class so the Unreal Header Tool generates reflection metadata for it. GENERATED_BODY() is a placeholder inside the class body that UHT expands into required boilerplate — type info, casting helpers, and accessors. Both are mandatory.
Common mistakes
- ✗Believing reflection metadata is built at runtime rather than by UHT at compile time
- ✗Thinking
GENERATED_BODY()is optional - ✗Placing
GENERATED_BODY()outside the class body
Follow-up questions
- →What goes wrong if you place
GENERATED_BODY()in the wrong access section? - →What is the difference between
GENERATED_BODY()and the olderGENERATED_UCLASS_BODY()?
MiddleTheoryCommonHow do UObject*, TObjectPtr, TWeakObjectPtr, TSoftObjectPtr, and TSubclassOf differ?
How do UObject*, TObjectPtr, TWeakObjectPtr, TSoftObjectPtr, and TSubclassOf differ?
UObject* and TObjectPtr are strong references that keep an object alive. TWeakObjectPtr is non-owning and goes null on collection. TSoftObjectPtr is a lazy path-based reference to a possibly unloaded asset. TSubclassOf is a type-safe class reference, not an instance.
Common mistakes
- ✗Thinking
TWeakObjectPtrkeeps its target alive - ✗Confusing
TSubclassOf(a class) with an instance pointer - ✗Believing a
TSoftObjectPtris always loaded and ready to dereference
Follow-up questions
- →How do you safely dereference a
TSoftObjectPtr? - →When would you replace a raw
UObject*withTObjectPtr?
JuniorTheoryOccasionalHow do you expose a value to designers but clamp it between 0 and 10?
How do you expose a value to designers but clamp it between 0 and 10?
Mark it UPROPERTY(EditAnywhere, meta = (ClampMin = "0", ClampMax = "10")). ClampMin/ClampMax hard-limit typed values; UIMin/UIMax bound the drag slider. The clamp is enforced by the editor, so code can still set out-of-range values.
Common mistakes
- ✗Expecting
ClampMin/ClampMaxto also clamp runtime assignments - ✗Confusing
ClampMin/ClampMaxwithUIMin/UIMax - ✗Thinking a hand-written setter is the only option
Follow-up questions
- →What does
UIMin/UIMaxchange compared toClampMin/ClampMax? - →How would you enforce the range at runtime as well?
MiddleTheoryOccasionalWhat is the difference between BlueprintImplementableEvent and BlueprintNativeEvent?
What is the difference between BlueprintImplementableEvent and BlueprintNativeEvent?
BlueprintImplementableEvent has no C++ body — it is implemented entirely in Blueprint. BlueprintNativeEvent has a C++ default implementation (an _Implementation function) that a Blueprint may override. Use Native when you need a fallback in code.
Common mistakes
- ✗Writing a C++ body for a
BlueprintImplementableEvent(it must have none) - ✗Forgetting the
_Implementationsuffix for aBlueprintNativeEvent - ✗Confusing which one provides a C++ default
Follow-up questions
- →How does a Blueprint call the C++ default of a
BlueprintNativeEvent? - →What linker error appears if you define the wrong function name?
MiddleTheoryOccasionalWhy must you be careful with gameplay logic in a UObject constructor?
Why must you be careful with gameplay logic in a UObject constructor?
The constructor also runs on the CDO at editor and load time, when there is no world, no other actors, and no gameplay context. Code that spawns actors, reads the world, or assumes runtime state will misbehave or crash. Put such logic in BeginPlay.
Common mistakes
- ✗Forgetting the constructor runs on the CDO without a world
- ✗Spawning actors or accessing
GetWorld()from a constructor - ✗Confusing the constructor's role with
BeginPlay's
Follow-up questions
- →What is safe to do in a constructor — component creation or world queries?
- →Where should logic that needs a valid world go instead?
MiddleTheoryOccasionalWhy does Unreal need its own reflection system instead of C++ RTTI?
Why does Unreal need its own reflection system instead of C++ RTTI?
C++ RTTI only provides type identity and dynamic_cast; it cannot enumerate members or read metadata. Unreal's reflection exposes properties and functions by name, powering the editor, Blueprints, networking, and serialization — capabilities standard C++ has no equivalent for.
Common mistakes
- ✗Believing C++ RTTI can enumerate class members
- ✗Thinking the reflection system exists only to replace
dynamic_cast - ✗Assuming reflection is just a performance optimization over RTTI
Follow-up questions
- →What information does
UClasshold thatstd::type_infodoes not? - →How does reflection enable Blueprint-to-C++ calls?
MiddleTheoryOccasionalWhen should you use a TSoftObjectPtr instead of a hard reference?
When should you use a TSoftObjectPtr instead of a hard reference?
Use TSoftObjectPtr when you want to reference an asset without forcing it into memory. A hard reference loads the whole dependency chain with the owner. A soft reference defers loading, cutting memory footprint and load times for optional or large assets.
Common mistakes
- ✗Assuming a soft reference is automatically resolved when accessed
- ✗Using hard references for large optional assets and bloating memory
- ✗Thinking soft vs hard is about access speed rather than load timing
Follow-up questions
- →How does
FStreamableManagerload a soft reference asynchronously? - →What is the difference between
TSoftObjectPtrandTSoftClassPtr?
MiddleTheoryOccasionalHow does the Unreal Header Tool work at a high level?
How does the Unreal Header Tool work at a high level?
Before the C++ compiler runs, UHT parses headers containing UCLASS/UPROPERTY/UFUNCTION macros and generates .generated.h and .gen.cpp files holding the reflection tables and boilerplate. The normal compiler then builds your code together with the generated code.
Common mistakes
- ✗Thinking UHT runs at runtime or game startup
- ✗Believing UHT replaces the C++ compiler
- ✗Not knowing the
.generated.hinclude is required and must be last
Follow-up questions
- →Why must
#include "X.generated.h"be the last include in a header? - →What build error appears if a
UPROPERTYtype is not reflectable?
SeniorTheoryOccasionalWhy does editing a Blueprint default sometimes not change an already-placed instance?
Why does editing a Blueprint default sometimes not change an already-placed instance?
Each placed instance stores only a delta against the CDO — properties it explicitly overrode. Editing the Blueprint default changes the CDO, and that new value propagates only to properties an instance has not overridden. Once a designer touched a property on the instance, it has its own delta and ignores the CDO. Reset-to-default clears the delta.
Common mistakes
- ✗Thinking an instance stores a full value copy rather than a delta against the CDO
- ✗Assuming a default edit propagates to every instance regardless of local overrides
- ✗Not knowing reset-to-default removes the instance delta and re-links it to the CDO
Follow-up questions
- →How does the editor decide which properties to show as bold (overridden)?
- →What happens to instance deltas when you reparent the Blueprint?
SeniorPerformanceRareHow do TObjectPtr, TWeakObjectPtr, and TSoftObjectPtr differ in GC cost at scale?
How do TObjectPtr, TWeakObjectPtr, and TSoftObjectPtr differ in GC cost at scale?
A UPROPERTY TObjectPtr is a strong reference: the GC traverses it every reachability pass and it keeps the target alive, so millions of them lengthen the mark phase. TWeakObjectPtr is not traversed and does not keep anything alive — cheap to scan, resolved by a serial-number check. TSoftObjectPtr is just a path, never traced, and may not be loaded at all.
Common mistakes
- ✗Believing
TWeakObjectPtris traced by the GC and so prolongs the mark phase - ✗Thinking a
TWeakObjectPtrkeeps its target alive like a strong reference - ✗Assuming reference count is irrelevant because the GC only walks the property table
Follow-up questions
- →How does a
TWeakObjectPtrdetect that its target has been destroyed? - →When does converting strong references to weak actually reduce GC pass time?
SeniorDebuggingRareWhat reflection traps make Live Coding / hot-reload unreliable for UObject changes?
What reflection traps make Live Coding / hot-reload unreliable for UObject changes?
Live Coding patches machine code in place but cannot rebuild reflection: a new or renamed UPROPERTY, a changed UCLASS layout, or an altered vtable need a fresh .generated.h from UHT, which only a full editor restart applies. Patching anyway leaves stale vtables, properties the GC and serializer cannot see, and often a GC assert. Header and CDO changes always require a restart.
Common mistakes
- ✗Adding a
UPROPERTYduring a Live Coding session and expecting it to be GC-visible - ✗Assuming Live Coding reruns UHT, so reflection metadata stays in sync with the patch
- ✗Treating a hot-reload GC assert as random instead of a stale layout symptom
Follow-up questions
- →Which kinds of edit are genuinely safe to apply through Live Coding?
- →Why can a stale vtable survive a patch and cause a wrong virtual call?
SeniorDebuggingRareHow do you rename or retype a UPROPERTY without breaking already-saved assets?
How do you rename or retype a UPROPERTY without breaking already-saved assets?
Saved assets store property values keyed by reflected name, so a plain rename silently drops the old value. Add a CoreRedirects entry in DefaultEngine.ini mapping old name to new so the loader rebinds it. A type change is not redirectable: implement Serialize or PostLoad with a custom version (FCustomVersion) to read the old layout and convert it.
Common mistakes
- ✗Renaming a
UPROPERTYin code and assuming saved values follow the new name - ✗Believing
CoreRedirectscan also handle a type change, not just a rename - ✗Forgetting to bump a custom version, so old and new data cannot be told apart on load
Follow-up questions
- →Where do you register an
FCustomVersionand how doesPostLoadread it? - →How do property redirects differ from class and enum redirects?