Creating SDL2 Buttons

Understanding Incomplete Types in C++ Forward Declarations

What is an "incomplete type" in C++ and why does it prevent calling functions in a header file?

Abstract art representing computer programming

When we forward-declare a class, like we did with class UI; in Button.h, we're telling the compiler, "Trust me, there's a class named UI defined somewhere else."

// Button.h
#pragma once
#include <SDL.h>
#include "Rectangle.h"

class UI; // Forward declaration

class Button : public Rectangle {
 public:
  // ...
  // We can USE the name UI here:
  Button(UI& UIManager, const SDL_Rect& Rect);
private:
  // We can declare members of this type:
  UI& UIManager;
};

At this point, inside Button.h, UI is considered an incomplete type. The compiler knows the name UI refers to a class type, but it doesn't know anything else about it:

  • It doesn't know how big UI is (its size in memory).
  • It doesn't know what member variables UI has.
  • It doesn't know what member functions UI has.

What Can You Do with an Incomplete Type?

Because the compiler lacks detailed information, there are limitations on what you can do with an incomplete type directly within the header file that only sees the forward declaration:

  • You cannot create an instance of UI (e.g., UI myUI;). The compiler needs to know the size.
  • You cannot use sizeof(UI).
  • You cannot access members of UI (e.g., UIManager.someMember). The compiler doesn't know if someMember exists.
  • You cannot call methods of UI (e.g., UIManager.SomeFunction()). The compiler doesn't know if SomeFunction exists or what its signature is.
  • You cannot inherit from UI.
  • You cannot use it as a base type for dynamic_cast.

However, you can do things that don't require knowing the class's internal details:

  • You can declare pointers or references to UI (e.g., UI* ptr; or UI& ref;). Pointers and references usually have a fixed size regardless of the type they point/refer to.
  • You can declare functions or methods that take or return pointers or references to UI (e.g., void ProcessUI(UI* uiPtr); or UI& GetUIManager();).

Why It Prevents Calling Functions in Headers

In our lesson, the Button class needed to call the SetRectangleColors() method on its UIManager reference.

// Button.h - Trying to DEFINE OnLeftClick here
// ...
class Button : public Rectangle {
 public:
  Button(UI& UIManager, const SDL_Rect& Rect);

  // Attempting definition in the header:
  void OnLeftClick() override {
    // ERROR! UI is an incomplete type here!
    UIManager.SetRectangleColors({0,255,0,255}); 
  }
private:
  UI& UIManager;
};

This fails because when the compiler processes Button.h, it only sees class UI;. It has no idea if UI actually has a method called SetRectangleColors or what arguments it takes.

The Solution: Move Definition to .cpp

The solution is to only declare the function in the header and define it in a corresponding source file (.cpp).

// Button.h - Declaration Only
// ...
class Button : public Rectangle {
 public:
  Button(UI& UIManager, const SDL_Rect& Rect);
  void OnLeftClick() override; // Declaration 
private:
  UI& UIManager;
};
// Button.cpp - Definition
#include "Button.h"
#include "UI.h" // Include the full definition! 

void Button::OnLeftClick() {
  // OK! Compiler now sees the full definition
  // of UI from UI.h and knows about the method.
  UIManager.SetRectangleColors(
    {0, 255, 0, 255} // Green
  );
}

In Button.cpp, we #include "UI.h". This gives the compiler the full definition of the UI class before it compiles the Button::OnLeftClick method. Now, UI is a complete type, and the compiler can verify that SetRectangleColors exists and is being called correctly.

Forward declarations and incomplete types are essential tools for breaking circular dependencies between headers and reducing compilation times, but they require us to separate declarations (in .h) from definitions that require complete types (in .cpp).

Answers to questions are automatically generated and may not have been reviewed.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

This course includes:

  • 110 Lessons
  • 92% Positive Reviews
  • Regularly Updated
  • Help and FAQs
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2025 - All Rights Reserved