Think of C++ compilation like building a house. Header files (.h) are the blueprints that describe what rooms exist, while source files (.cpp) contain the actual construction details. This separation is crucial for large projects like games.
A well-structured header file is like a contract - it tells other code what's available without revealing implementation details.
// Player.h - Header file example
#pragma once // Modern include guard (or use #ifndef/#define/#endif)
// Forward declarations instead of includes when possible
class AWeapon; // Just need to know it exists
class UHealthComponent;
struct FDamageInfo;
// Only include what you must
#include "CoreMinimal.h" // Unreal's core types
#include "GameFramework/Character.h" // Parent class
#include "Player.generated.h" // Unreal's generated code
// Class declaration
class MYGAME_API APlayer : public ACharacter {
GENERATED_BODY() // Unreal macro
public:
// Constructor/Destructor
APlayer();
virtual ~APlayer();
// Public interface
void EquipWeapon(AWeapon* NewWeapon);
float GetHealthPercent() const;
// Inline functions (defined in header)
FORCEINLINE int32 GetLevel() const { return Level; }
FORCEINLINE bool IsAlive() const { return Health > 0.0f; }
protected:
// Protected members
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
private:
// Private data
UPROPERTY()
AWeapon* CurrentWeapon; // Forward declared type
UPROPERTY()
UHealthComponent* HealthComp;
float Health;
int32 Level;
// Private methods
void UpdateHealthBar();
};
// Traditional include guards (alternative to #pragma once)
/*
#ifndef PLAYER_H
#define PLAYER_H
// ... header content ...
#endif // PLAYER_H
*/
The source file contains the actual code - the construction work based on the blueprint.
// Player.cpp - Source file
#include "Player.h" // Always include own header first
// Now include headers only needed for implementation
#include "Weapon.h" // Full definition needed here
#include "HealthComponent.h"
#include "Engine/World.h"
#include "Components/CapsuleComponent.h"
// Constructor implementation
APlayer::APlayer() {
// Set defaults
Health = 100.0f;
Level = 1;
// Create components
HealthComp = CreateDefaultSubobject(TEXT("HealthComp"));
}
// Destructor
APlayer::~APlayer() {
// Cleanup if needed (rare in Unreal due to GC)
}
// Method implementations
void APlayer::EquipWeapon(AWeapon* NewWeapon) {
// Implementation details hidden from header
if (CurrentWeapon) {
CurrentWeapon->OnUnequipped();
}
CurrentWeapon = NewWeapon;
if (CurrentWeapon) {
CurrentWeapon->OnEquipped(this);
}
}
float APlayer::GetHealthPercent() const {
return Health / 100.0f;
}
void APlayer::BeginPlay() {
Super::BeginPlay();
// Initialization code
UpdateHealthBar();
}
void APlayer::UpdateHealthBar() {
// Private method implementation
// UI update code here
}
Forward declarations are like business cards - they tell the compiler "this thing exists" without providing full details. This reduces compilation time and breaks circular dependencies.
// Forward declaration rules and examples
// 1. When you only need pointers or references
class AWeapon; // Forward declaration
class APlayer {
AWeapon* CurrentWeapon; // OK - pointer
void EquipWeapon(AWeapon* Weapon); // OK - parameter
AWeapon& GetWeapon(); // OK - reference return
// AWeapon DirectWeapon; // ERROR - needs full definition
};
// 2. Function declarations
class UDamageType; // Forward declaration
float CalculateDamage(AActor* Target, UDamageType* DamageType); // OK
// 3. Template forward declarations
template class TArray; // Forward declare template
void ProcessArray(TArray* Array); // OK - pointer to template
// 4. Breaking circular dependencies
// Weapon.h
class APlayer; // Forward declare
class AWeapon {
APlayer* Owner; // OK
};
// Player.h
class AWeapon; // Forward declare
class APlayer {
AWeapon* Weapon; // OK
};
// 5. Nested class forward declarations
class Outer {
class Inner; // Forward declare nested class
Inner* InnerPtr; // OK
};
// 6. When you CAN'T use forward declarations
class Base; // Forward declaration
// ERROR: Can't inherit from forward declared class
// class Derived : public Base {};
// ERROR: Can't use sizeof with forward declared type
// size_t size = sizeof(Base);
// ERROR: Can't call methods on forward declared type
// void UseBase(Base* b) { b->Method(); }
Managing includes is like organizing your toolbox - keep only what you need at hand to work efficiently.
// GameMode.h - Optimized header
#pragma once
// Minimal includes - only what's absolutely necessary
#include "GameFramework/GameModeBase.h"
#include "GameMode.generated.h"
// Forward declarations for everything else
class APlayerController;
class APlayerState;
class APawn;
class ASpawnPoint;
class UGameInstance;
struct FGameStats;
template class TSubclassOf;
// Use UCLASS macro
UCLASS()
class MYGAME_API AMyGameMode : public AGameModeBase {
GENERATED_BODY()
public:
AMyGameMode();
// Methods using forward declared types
void RespawnPlayer(APlayerController* PC);
void UpdateGameStats(const FGameStats& Stats);
// Template with forward declaration
void RegisterSpawnPoint(ASpawnPoint* Point);
protected:
// Unreal's TSubclassOf works with forward declarations
UPROPERTY(EditDefaultsOnly, Category = "Classes")
TSubclassOf DefaultPawnClass;
// Pointers to forward declared types
UPROPERTY()
TArray SpawnPoints;
private:
// Even private members can use forward declared types
APlayerController* LastSpawnedController;
FGameStats* CurrentStats;
};
Precompiled headers are like pre-assembled components - commonly used parts are compiled once and reused, speeding up build times significantly.
// MyProjectPCH.h - Precompiled header
#pragma once
// Include expensive headers that rarely change
#include
#include
#include
#include
#include
// Platform headers
#ifdef _WIN32
#include
#endif
// Engine headers (in Unreal)
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "GameFramework/Actor.h"
// DON'T include frequently changing headers!
// #include "MyGameTypes.h" // Bad - changes often
// Using PCH in your files
// MyClass.cpp
#include "MyProjectPCH.h" // Must be first include
#include "MyClass.h" // Then your header
// ... rest of includes ...
Understanding common pitfalls helps you avoid hours of debugging compilation errors.
// Problem 1: Circular Dependencies
// Character.h
#pragma once
#include "Weapon.h" // Problem: Weapon.h includes Character.h!
class ACharacter {
AWeapon Weapon; // Requires full definition
};
// Solution: Use forward declaration and pointers
// Character.h
#pragma once
class AWeapon; // Forward declaration
class ACharacter {
AWeapon* Weapon; // Pointer doesn't need full definition
};
// Problem 2: Missing Include Guards
// Bad.h - Missing guards causes redefinition errors
class MyClass {
// ...
};
// Good.h - With include guards
#pragma once // or #ifndef/#define/#endif
class MyClass {
// ...
};
// Problem 3: Including in Header When CPP Would Suffice
// Bad: Widget.h
#pragma once
#include "ComplexSystem.h" // 1000+ lines included everywhere!
class Widget {
void ProcessData();
};
// Good: Widget.h
#pragma once
class ComplexSystem; // Forward declare
class Widget {
void ProcessData();
};
// Widget.cpp
#include "Widget.h"
#include "ComplexSystem.h" // Include only where needed
// Problem 4: Inline Function Dependencies
// Bad: Forces everyone to include "Math.h"
// GameObject.h
#pragma once
#include "Math.h" // Expensive include
class GameObject {
Vector3 Position;
public:
Vector3 GetPosition() { return Position; } // Inline needs Vector3
};
// Good: Move to CPP or forward declare
// GameObject.h
#pragma once
class GameObject {
class Vector3* Position; // Or use pimpl idiom
public:
Vector3 GetPosition(); // Not inline
};
Unreal Engine has specific requirements for headers that you must follow.
// Unreal Engine header requirements
// 1. Generated header must be last
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
// Other includes...
#include "MyActor.generated.h" // ALWAYS LAST!
// 2. Module API macro
class MYMODULE_API AMyActor : public AActor {
// MYMODULE_API exports the class from the module
};
// 3. IWYU (Include What You Use) pragma
#include "GameFramework/Actor.h" // IWYU pragma: export
// 4. Forward declarations in Unreal
class AActor; // OK
class MYMODULE_API AMyActor; // Include API macro
// 5. Common Unreal forward declarations
class UWorld;
class UObject;
class AActor;
class APawn;
class AController;
class UActorComponent;
// 6. Unreal's precompiled header
// YourProject.h (PCH)
#pragma once
#include "CoreMinimal.h" // Includes most common types
// YourFile.cpp
#include "YourProject.h" // Include PCH first
#include "YourFile.h" // Then your header
The following header file has multiple issues. Fix them using forward declarations and proper organization:
// GameCharacter.h - FIX THIS FILE
#include
#include
#include "Weapon.h"
#include "Inventory.h"
#include "CharacterStats.h"
#include "AnimationSystem.h"
#include "ParticleEffects.h"
#include "SoundManager.h"
#include "UIHealthBar.h"
class GameCharacter {
private:
std::string name;
Weapon currentWeapon;
Inventory inventory;
CharacterStats stats;
AnimationSystem* animator;
ParticleEffects effects;
SoundManager sounds;
UIHealthBar* healthBar;
public:
GameCharacter() {
animator = new AnimationSystem();
healthBar = new UIHealthBar();
}
void Attack() {
currentWeapon.Fire();
effects.PlayMuzzleFlash();
sounds.PlaySound("attack");
}
std::vector- GetAllItems() {
return inventory.GetItems();
}
};
// Issues to fix:
// 1. Too many includes
// 2. STL types in interface
// 3. Implementation in header
// 4. No include guards
// 5. Missing forward declarations
// 6. Value members that could be pointers
Remember: Every header you include affects compilation time. Think of headers as expensive imports - only include what you absolutely need!