Unreal C++ is standard C++ augmented by a reflection layer generated by the Unreal Header Tool (UHT). Getting the reflection macros right determines whether your code compiles, appears in Blueprints, replicates correctly, and survives garbage collection.
When to use this skill
- Creating or editing any
U*/A*class orF*struct in C++. - Exposing a member or function to Blueprints, the editor, or network replication.
- Diagnosing UHT errors ("Unrecognized type", "missing
generated.h", "GENERATED_BODY missing"). - Deciding between
TObjectPtr, raw pointer,TWeakObjectPtr, orTSharedPtr. - Understanding how the CDO works or why a constructor runs in the editor.
Mental model
Three interlocking pieces:
- UObject is the base of the reflected, garbage-collected object graph. Every class that opts
into the system derives from it (directly or via
AActor,UActorComponent, etc.). - UHT parses your headers before the compiler and emits a
<ClassName>.generated.hthat contains boilerplate for reflection, serialization, and Blueprint integration. You include this file last and placeGENERATED_BODY()inside the type. - UClass (the runtime type descriptor) holds a Class Default Object (CDO) — one instance constructed at startup with all defaults applied. New instances copy from the CDO. The constructor runs on the CDO and in the editor; never put gameplay logic there.
UObjects are garbage-collected. Any UObject* you want kept alive must be stored in a UPROPERTY.
A plain C++ pointer is invisible to the GC and will dangle after the next collection.
Class prefixes (mandatory)
| Prefix | Meaning | Examples |
|---|---|---|
U |
UObject-derived (non-Actor) | UActorComponent, UMyDataAsset |
A |
Actor-derived (world-placeable) | AActor, AMyCharacter |
F |
Plain struct / non-UObject value type | FVector, FMyConfig |
E |
Enum | EMyState |
I |
Interface (the IFoo half of a UFoo/IFoo pair) |
IInteractable |
T |
Template | TArray, TObjectPtr |
S |
Slate widget | SButton |
The class name after the prefix must match the filename (minus prefix): AMyPawn lives in
MyPawn.h / MyPawn.cpp.
Anatomy of a UObject class
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h" // ALWAYS LAST include
UCLASS(Blueprintable, BlueprintType)
class MYGAME_API AMyActor : public AActor // MYGAME_API = module export macro
{
GENERATED_BODY()
public:
AMyActor();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float Health = 100.f;
UFUNCTION(BlueprintCallable, Category = "Stats")
void ApplyDamage(float Amount);
protected:
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere)
TObjectPtr<USceneComponent> Root; // UPROPERTY keeps it alive in the GC graph
};
// MyActor.cpp
#include "MyActor.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
}
void AMyActor::BeginPlay()
{
Super::BeginPlay(); // call Super on all engine virtual overrides
}
void AMyActor::ApplyDamage(float Amount)
{
Health = FMath::Max(0.f, Health - Amount);
}
Four rules that prevent most build breaks:
#include "X.generated.h"is the last include in the header.- Every reflected class/struct has
GENERATED_BODY(). - The class is annotated with the module API macro (
MYGAME_API) so other modules can link it. - Always call
Super::in overridden engine virtuals (BeginPlay,Tick,EndPlay, …).
UCLASS specifiers
Verified in ObjectMacros.h (namespace UC, line 792):
Blueprintable— the class can be subclassed in Blueprint.BlueprintType— can be used as a Blueprint variable type.Abstract— cannot be instantiated directly.Config=Game— properties markedConfigare read/written from*.inifiles.MinimalAPI— exports only the reflection boilerplate (Cast<>, etc.); use in leaf-module classes to reduce link surface.meta=(PrioritizeCategories="Stats")— editor ordering hints.
UPROPERTY specifiers
Verified in ObjectMacros.h (namespace UP, line 1046):
Edit / visibility (Details panel):
EditAnywhere— editable on CDO and placed instances.EditDefaultsOnly— CDO (Blueprint defaults) only.EditInstanceOnly— placed instances only.VisibleAnywhere/VisibleDefaultsOnly/VisibleInstanceOnly— read-only in the editor.
Blueprint access:
BlueprintReadWrite— get and set in BP.BlueprintReadOnly— get only.
Common combos and meta:
Category = "Group"— organizes the Details panel (required for every exposed member).meta=(ClampMin="0", ClampMax="100")— value ranges.meta=(AllowPrivateAccess="true")— expose aprivatemember to Blueprint.Transient— not saved to disk.SaveGame— included in SaveGame serialization.Replicated/ReplicatedUsing=OnRep_Func— network replication (seenetworking-and-replication).Instanced— for per-instance configurable subobjects.
Memory rule: any UObject* member you want kept alive must be a UPROPERTY. Use
TObjectPtr<UType> for class members (editor-aware, access-tracked in UE5); raw UType* is
acceptable for local variables and function parameters. Without UPROPERTY, the GC can collect the
object and leave a dangling pointer. See memory-and-gc for the full pointer hierarchy.
UFUNCTION specifiers
Verified in ObjectMacros.h (namespace UF, line 945):
BlueprintCallable— callable from Blueprint event graphs.BlueprintPure— no side effects; pure node (no exec pins).BlueprintImplementableEvent— declared in C++, fully implemented in Blueprint; no C++ body.BlueprintNativeEvent— C++ default (Func_Implementation) overridable in Blueprint.CallInEditor— adds a button in the Details panel for editor-time execution.Exec— registers as a console command.Server/Client/NetMulticast+Reliable/Unreliable— RPCs.
BlueprintNativeEvent pattern:
UFUNCTION(BlueprintNativeEvent, Category="AI")
void OnSpotted(AActor* By);
void OnSpotted_Implementation(AActor* By); // C++ default, override in BP
USTRUCT, UENUM, UINTERFACE
// Value type struct — no GC, stack/value semantics
USTRUCT(BlueprintType)
struct FLoadout
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Loadout")
int32 Ammo = 0;
};
// Enum — uint8-backed required for Blueprint use
UENUM(BlueprintType)
enum class EDoorState : uint8 { Closed, Opening, Open, Closing };
// Interface: always declare both halves (U* is the UObject shell; I* is the C++ interface)
UINTERFACE(MinimalAPI, Blueprintable)
class UInteractable : public UInterface { GENERATED_BODY() };
class IInteractable
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent, Category="Interaction")
void Interact(AActor* Instigator);
};
Key differences between USTRUCT and UCLASS:
UScriptStruct(the runtime descriptor for USTRUCT) derives fromUStruct, notUClass. Struct instances are value types — no GC, no CDO, noNewObject.UPROPERTYinside a struct still enables serialization and editor exposure; it does not imply GC ownership (there is nothing for the GC to track in a value type).- Prefer
USTRUCTfor lightweight data bags (stats, configs, hit results). UseUCLASSwhen you need GC lifetime, Blueprint subclassing, or per-instance identity.
See references/reflection-macros.md for the full specifier reference with less-common options.
Object construction
| API | When | Notes |
|---|---|---|
CreateDefaultSubobject<T>(TEXT("Name")) |
Constructor only | Creates owned subobject; names must be unique per class |
NewObject<T>(Outer) |
Runtime, non-Actor | Use for any UObject that is not an Actor |
GetWorld()->SpawnActor<T>(Class, FTransform) |
Runtime, Actors | See actors-and-components |
Cast<T>(Obj) |
Anywhere | Safe checked cast; returns null on failure — never static_cast |
IsValid(Obj) |
Anywhere | True if non-null and not garbage; prefer over bare null check |
CreateDefaultSubobject is declared on UObject (Object.h:151). NewObject template
overloads are in UObjectGlobals.h (lines 1931, 1959, 1974).
The CDO is created by UClass::GetDefaultObject (Class.h:4519) the first time it is needed.
After construction the CDO is read-only; modify only through archetype/default propagation.
Full construction paths, CDO lifecycle, and PostInitProperties are in
references/uobject-lifecycle-and-cdo.md.
Pointer and ownership cheat sheet
| Need | Use |
|---|---|
| UObject member you own / keep alive | UPROPERTY() TObjectPtr<UType> |
| Local variable or function parameter | raw UType* |
| Non-owning ref that may be destroyed | TWeakObjectPtr<UType> — check .IsValid() before use |
| Non-UObject heap object, shared ownership | TSharedPtr<T> / TSharedRef<T> |
| Asset reference loaded on demand | TSoftObjectPtr<T> / TSoftClassPtr<T> |
TObjectPtr<T> (declared in ObjectPtr.h:519) is the modern form for UPROPERTY members in UE5.
Older code uses raw T* UPROPERTY() pointers, which still compile and work. Legacy code you
encounter will mix both styles.
Gotchas
- Forgot
UPROPERTYon a UObject member* → random crashes after GC. The most common bug. generated.hnot last / missingGENERATED_BODY()→ cryptic UHT errors that appear unrelated to the actual mistake.- Gameplay logic in the constructor → runs on the CDO and in the editor; no world, no
gameplay. Always put gameplay init in
BeginPlay. CreateDefaultSubobjectoutside the constructor → asserts (detected byFObjectInitializer::AssertIfInConstructor,UObjectGlobals.h:1936).NewObjectwith an empty name inside a constructor → also asserts; useCreateDefaultSubobjectinstead.- Calling editor-only API from runtime code → packaging failures; guard with
#if WITH_EDITOR. UENUMnot backed byuint8→ Blueprint-unusable;BlueprintTypeenum must beuint8.ClassDefaultObjectaccessed directly → deprecated as of UE 5.6; useGetDefaultObject()/GetDefault<T>()instead (Class.h:4045).FName/FString/FTextmix-ups → seecore-types-and-containers.
Version notes
TObjectPtr<T>(UPROPERTY member idiom) andTObjectPtr-backed GC barrier were introduced in UE5. Pre-5.0 code uses rawT* UPROPERTY(), which still works but lacks editor access tracking.MarkPendingKill()was replaced byMarkAsGarbage()in UE5; withgc.PendingKillEnabled=false(the new default), the GC no longer auto-nulls pointers — useIsValid()and clear references inEndPlay/callbacks.ClassDefaultObjectwas deprecated as a direct field access in 5.6; useGetDefaultObject().
References & source material
Engine source (UE 5.8, under Engine/Source/Runtime/CoreUObject/Public/UObject/):
Object.h:98 —UObjectclass definition;:151—CreateDefaultSubobject;:226—PostInitProperties;:361—BeginDestroy;:368—IsReadyForFinishDestroy;:382—FinishDestroy;:1886—IsValid().ObjectMacros.h:778 —UPROPERTY/UFUNCTION/USTRUCT/UENUMmacro stubs;:800—GENERATED_BODY;:832—UCLASSspecifiers;:985—UFUNCTIONspecifiers;:1086—UPROPERTYspecifiers;:1216—USTRUCTspecifiers.Class.h:495 —UStruct;:1774—UScriptStruct;:3893—UClass;:4045— deprecatedClassDefaultObjectfield (5.6+);:4519—GetDefaultObject().UObjectGlobals.h:1931 —NewObject<T>(Outer, Class, Name, …);:1959—NewObject<T>(Outer)(no name);:1974—NewObject<T>(Outer, Name, …).ObjectPtr.h:519 —TObjectPtr<T>.Interface.h:18 —UInterfacebase class.
Official docs (UE 5.8):
- Objects — https://dev.epicgames.com/documentation/unreal-engine/objects-in-unreal-engine
- UObject Instance Creation — https://dev.epicgames.com/documentation/unreal-engine/creating-objects-in-unreal-engine
- Unreal Object Handling — https://dev.epicgames.com/documentation/unreal-engine/unreal-object-handling-in-unreal-engine
- Properties — https://dev.epicgames.com/documentation/unreal-engine/unreal-engine-uproperties
- UFunctions — https://dev.epicgames.com/documentation/unreal-engine/ufunctions-in-unreal-engine
- Unreal Interfaces — https://dev.epicgames.com/documentation/unreal-engine/interfaces-in-unreal-engine
- Metadata Specifiers — https://dev.epicgames.com/documentation/unreal-engine/metadata-specifiers-in-unreal-engine
Deep-dive references in this skill:
- references/reflection-macros.md — full specifier tables for UCLASS/UPROPERTY/UFUNCTION/USTRUCT/UENUM/UINTERFACE with less-common options.
- references/uobject-lifecycle-and-cdo.md — CDO
construction,
PostInitProperties, GC callbacks,BeginDestroy/FinishDestroy. - references/object-creation-and-gc.md —
NewObjectvsCreateDefaultSubobject, object flags, GC roots, clustering, and ownership patterns.
Cross-reference sibling skills:
memory-and-gc— deep dive on pointer types, weak pointers, GC tuning.actors-and-components— Actor/component lifecycle, spawning, ticking.blueprint-cpp-integration— exposing C++ types to Blueprint in depth.module-and-build-system— the module API macro,Build.cs, UHT pipeline details.