# Ue Cpp Fundamentals

> Write correct Unreal Engine C++ using the UObject reflection system — UCLASS/USTRUCT/ UENUM/UINTERFACE macros, UPROPERTY and UFUNCTION specifiers, GENERATED_BODY, the *.generated.h pipeline, class prefixes (U/A/F/E/I), module API export macros, the Class Default Object (CDO), NewObject vs CreateDefaultSubobject, garbage-collection-safe ownership, and UClass vs UScriptStruct internals. Use when authoring or editing any UE C++ class, exposing members or functions to Blueprints or replication, fixing UHT/reflection build errors, or choosing between pointer and ownership types.

- Skill: `kevinpbuckley/ue-cpp-fundamentals` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add kevinpbuckley/ue-cpp-fundamentals`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kevinpbuckley/ue-cpp-fundamentals/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: kevinpbuckley (https://skillmd.com/u/kevinpbuckley)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kevinpbuckley/ue-cpp-fundamentals

---


# Unreal Engine C++ fundamentals

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 or `F*` 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`, or `TSharedPtr`.
- Understanding how the CDO works or why a constructor runs in the editor.

## Mental model

Three interlocking pieces:

1. **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.).
2. **UHT** parses your headers before the compiler and emits a `<ClassName>.generated.h` that
   contains boilerplate for reflection, serialization, and Blueprint integration. You include this
   file **last** and place `GENERATED_BODY()` inside the type.
3. **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

```cpp
// 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
};
```

```cpp
// 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 marked `Config` are read/written from `*.ini` files.
- `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 a `private` member to Blueprint.
- `Transient` — not saved to disk. `SaveGame` — included in SaveGame serialization.
- `Replicated` / `ReplicatedUsing=OnRep_Func` — network replication (see `ue-networking-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 `ue-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:

```cpp
UFUNCTION(BlueprintNativeEvent, Category="AI")
void OnSpotted(AActor* By);
void OnSpotted_Implementation(AActor* By);   // C++ default, override in BP
```

## USTRUCT, UENUM, UINTERFACE

```cpp
// 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 from `UStruct`, not `UClass`.
  Struct instances are **value types** — no GC, no CDO, no `NewObject`.
- `UPROPERTY` inside 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 `USTRUCT` for lightweight data bags (stats, configs, hit results). Use `UCLASS` when you
  need GC lifetime, Blueprint subclassing, or per-instance identity.

See [references/reflection-macros.md](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 `ue-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](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 `UPROPERTY` on a UObject* member** → random crashes after GC. The most common bug.
- **`generated.h` not last / missing `GENERATED_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`.
- **`CreateDefaultSubobject` outside the constructor** → asserts (detected by
  `FObjectInitializer::AssertIfInConstructor`, `UObjectGlobals.h`:1936).
- **`NewObject` with an empty name inside a constructor** → also asserts; use
  `CreateDefaultSubobject` instead.
- **Calling editor-only API from runtime code** → packaging failures; guard with `#if WITH_EDITOR`.
- **`UENUM` not backed by `uint8`** → Blueprint-unusable; `BlueprintType` enum must be `uint8`.
- **`ClassDefaultObject` accessed directly** → deprecated as of UE 5.6; use
  `GetDefaultObject()`/`GetDefault<T>()` instead (`Class.h`:4045).
- **`FName`/`FString`/`FText` mix-ups** → see `ue-core-types-and-containers`.

## Version notes

- `TObjectPtr<T>` (UPROPERTY member idiom) and `TObjectPtr`-backed GC barrier were introduced in
  UE5. Pre-5.0 code uses raw `T* UPROPERTY()`, which still works but lacks editor access tracking.
- `MarkPendingKill()` was replaced by `MarkAsGarbage()` in UE5; with `gc.PendingKillEnabled=false`
  (the new default), the GC no longer auto-nulls pointers — use `IsValid()` and clear references
  in `EndPlay`/callbacks.
- `ClassDefaultObject` was deprecated as a direct field access in 5.6; use `GetDefaultObject()`.

## References & source material

Engine source (UE 5.8, under `Engine/Source/Runtime/CoreUObject/Public/UObject/`):
- `Object.h`:98 — `UObject` class definition; `:151` — `CreateDefaultSubobject`; `:226` —
  `PostInitProperties`; `:361` — `BeginDestroy`; `:368` — `IsReadyForFinishDestroy`; `:382` —
  `FinishDestroy`; `:1886` — `IsValid()`.
- `ObjectMacros.h`:778 — `UPROPERTY`/`UFUNCTION`/`USTRUCT`/`UENUM` macro stubs; `:800` —
  `GENERATED_BODY`; `:832` — `UCLASS` specifiers; `:985` — `UFUNCTION` specifiers; `:1086` —
  `UPROPERTY` specifiers; `:1216` — `USTRUCT` specifiers.
- `Class.h`:495 — `UStruct`; `:1774` — `UScriptStruct`; `:3893` — `UClass`; `:4045` —
  deprecated `ClassDefaultObject` field (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 — `UInterface` base 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](references/reflection-macros.md) — full specifier tables for
  UCLASS/UPROPERTY/UFUNCTION/USTRUCT/UENUM/UINTERFACE with less-common options.
- [references/uobject-lifecycle-and-cdo.md](references/uobject-lifecycle-and-cdo.md) — CDO
  construction, `PostInitProperties`, GC callbacks, `BeginDestroy`/`FinishDestroy`.
- [references/object-creation-and-gc.md](references/object-creation-and-gc.md) — `NewObject` vs
  `CreateDefaultSubobject`, object flags, GC roots, clustering, and ownership patterns.

Cross-reference sibling skills:
- `ue-memory-and-gc` — deep dive on pointer types, weak pointers, GC tuning.
- `ue-actors-and-components` — Actor/component lifecycle, spawning, ticking.
- `ue-blueprint-cpp-integration` — exposing C++ types to Blueprint in depth.
- `ue-module-and-build-system` — the module API macro, `Build.cs`, UHT pipeline details.

