Question

Fix Undefined Reference to vtable in C++

cppgccg++

Question

When building a C++ program, I receive an error similar to:

undefined reference to `vtable for CGameModule`

What causes this error, and how can it be fixed?

The error appears to occur while constructing CGameModule. I initially suspected that a virtual function was declared without a definition, but the available definitions seem complete.

// cgamemodule.h
class CGameModule : public Dasher::CDasherComponent {
public:
    CGameModule() : CDasherModule() {}
    virtual ~CGameModule();

    virtual void HandleEvent(Dasher::CEvent* pEvent);
};

// cgamemodule.cpp
#include "cgamemodule.h"

void CGameModule::HandleEvent(Dasher::CEvent* pEvent) {}

The base class is:

namespace Dasher {
    class CEvent;
    class CDasherComponent;
}

class Dasher::CDasherComponent {
public:
    CDasherComponent() {}
    virtual ~CDasherComponent() {}

    virtual void HandleEvent(Dasher::CEvent* pEvent) {}
};

Why does the linker report a missing vtable, and what definition is missing?

Short Answer

A vtable linker error usually means a polymorphic class has a declared virtual member function that has no linked definition. In this example, CGameModule::~CGameModule() is declared but never defined. Defining it, and ensuring its source file is part of the build, fixes the main issue.

Concept

A class becomes polymorphic when it has at least one virtual function. C++ uses runtime dispatch so that code can call the correct overridden function through a base-class pointer or reference.

For example:

Dasher::CDasherComponent* component = new CGameModule();
component->HandleEvent(event);

Even though the variable is a CDasherComponent*, C++ must be able to find CGameModule::HandleEvent at runtime. Compilers commonly implement this through a virtual table (vtable): an internal table containing pointers to virtual member functions.

A vtable is compiler-generated, but it contains references to your functions. If a virtual function is declared but its definition cannot be found by the linker, the linker may report:

undefined reference to `vtable for ClassName`

This message is often indirect: the vtable itself is not normally code you write. Instead, it signals that a required virtual function definition is missing or was not linked.

In the shown code, this declaration promises that a destructor implementation exists:

virtual ~CGameModule();

However, cgamemodule.cpp only defines HandleEvent. The destructor must also be defined, either in the .cpp file or inline in the class definition.

Mental Model

Think of a polymorphic class as a hotel with a directory at the front desk.

  • The vtable is the directory listing each virtual operation and where to find it.
  • Each virtual function definition is a room that must actually exist.
  • Declaring a virtual function says, “There is a room with this number.”
  • If the linker cannot find that room, it cannot complete the directory and reports a vtable-related error.

The error may appear when constructing an object because construction needs the object’s virtual dispatch setup. That does not necessarily mean the constructor is the real problem.

Take Quiz

Syntax and Examples

Declare virtual functions in a header and provide matching definitions for every non-pure virtual function that is used or needed by the program.

// cgamemodule.h
class CGameModule : public Dasher::CDasherComponent {
public:
    CGameModule() = default;
    ~CGameModule() override;

    void HandleEvent(Dasher::CEvent* pEvent) override;
};
// cgamemodule.cpp
#include "cgamemodule.h"

CGameModule::~CGameModule() = default;

void CGameModule::HandleEvent(Dasher::CEvent* pEvent) {
    // Handle the event.
}

= default asks the compiler to generate the normal destructor implementation, while still providing the required out-of-class definition.

You can also define the destructor with an empty body:

CGameModule::~CGameModule() {}

For a short class, an inline definition is valid too:

class CGameModule : public Dasher::CDasherComponent {
public:
    ~CGameModule() override = default;
};

Use override on overriding virtual functions. It is not required for virtual dispatch, but the compiler will catch signature mistakes such as an unintended parameter type or missing const.

Step by Step Execution

Consider this class:

class Animal {
public:
    virtual ~Animal();
    virtual void speak();
};

void Animal::speak() {}

int main() {
    Animal animal;
}

What happens:

  1. Animal has virtual functions, so it is polymorphic.
  2. Creating animal requires the program to support its virtual-function machinery.
  3. Animal::speak() has a definition, so that entry is available.
  4. Animal::~Animal() is declared, but no definition exists.
  5. During linking, the linker tries to resolve all required function symbols and the class’s virtual-function support.
  6. It cannot find Animal::~Animal(), and an error may be reported as an undefined destructor or an undefined reference to vtable for Animal.

Fix it by adding:

Animal::~Animal() = default;

For CGameModule, the corresponding missing definition is:

CGameModule::~CGameModule() = default;

Real World Use Cases

Virtual functions are common when a program works with a shared interface but uses different concrete implementations.

  • Game engines: a base Module, Entity, or Component class exposes virtual update or event methods.
  • User interfaces: a base widget class has virtual drawing and input-handling functions.
  • File and network systems: implementations of a common reader, writer, connection, or storage interface are selected at runtime.
  • Plugin systems: a host stores base-class pointers while plugins provide derived behavior.
  • Testing: fake implementations override a virtual interface used by production code.

In each case, every concrete class must have all of its declared virtual functions correctly defined and linked.

Take Quiz

Real Codebase Usage

In production C++ code, a common pattern is to put a virtual destructor declaration in a public header and define it in one .cpp file:

// processor.h
class Processor {
public:
    virtual ~Processor();
    virtual bool process();
};

// processor.cpp
#include "processor.h"

Processor::~Processor() = default;

This is especially useful for an interface or base class that is used across translation units.

Other practical rules:

  • Use override for derived implementations.
  • Make base destructors virtual when objects may be deleted through base pointers.
  • Ensure every .cpp containing an out-of-line virtual function is included in the target in CMake, Make, an IDE project, or another build system.
  • After adding or renaming source files, perform a clean rebuild if stale build artifacts are suspected.
  • Prefer = default when the compiler-generated behavior is correct; it documents intent and avoids an unnecessary empty function body.

A separate issue exists in the displayed constructor: CGameModule directly inherits from Dasher::CDasherComponent, but its initializer says CDasherModule().

CGameModule() : CDasherModule() {}

If there is no intermediate CDasherModule base class in the real code, this should be removed or changed. Direct base classes are initialized automatically when no initializer is needed:

CGameModule() = default;

If the real inheritance chain does include CDasherModule, the initializer may be correct, but that class must be a direct base.

Common Mistakes

Declaring a destructor without defining it

Broken:

class CGameModule {
public:
    virtual ~CGameModule();
};

If no definition is provided anywhere, linking fails when the destructor is required.

Fix:

CGameModule::~CGameModule() = default;

Defining a function with a different signature

These are different functions:

// Header
virtual void HandleEvent(Dasher::CEvent* event);

// Incorrect definition: const changes the signature
void CGameModule::HandleEvent(Dasher::CEvent* event) const {}

Use override in the header. It helps the compiler find mismatches.

void HandleEvent(Dasher::CEvent* event) override;

Forgetting to link the implementation file

A correct definition does not help if cgamemodule.cpp is not compiled and linked into the final executable or library.

For CMake, the source must belong to the target:

target_sources(my_program PRIVATE cgamemodule.cpp)

Confusing a pure virtual declaration with an implementation

A pure virtual function can make a class abstract:

virtual void run() = 0;

It does not automatically provide a definition. A pure virtual function may still need a definition in some designs, and a pure virtual destructor always needs one if destruction occurs.

Initializing a class that is not a direct base

A constructor initializer can only name direct bases or data members. If CDasherModule is not a direct base of CGameModule, do not initialize it there.

Treating the vtable as the root cause

The vtable is usually only the symptom. First inspect virtual destructors and virtual member functions for missing, mismatched, or unlinked definitions.

Comparisons

SituationMeaningTypical action
undefined reference to vtable for XA polymorphic class has unresolved virtual-function-related symbols, often a missing definition.Define and link all declared virtual functions, especially destructors.
undefined reference to X::~X()The linker directly identifies a missing destructor definition.Add X::~X() = default; or another valid definition.
Compile-time does not override errorA derived function signature does not match the base virtual function.Add override and correct the signature.
Pure virtual function error when constructingCode tries to instantiate an abstract class.Instantiate a concrete derived class that implements required functions.
Destructor formWhen to use it
virtual ~Base() = default; in the classSimple header-only or inline definition.
virtual ~Base(); plus Base::~Base() = default; in a .cpp fileCommon for non-header-only classes and library boundaries.
Non-virtual destructorOnly safe when objects will never be deleted through a base pointer.
Take Quiz

Cheat Sheet

class Derived : public Base {
public:
    ~Derived() override;              // Declaration
    void action(int value) override;  // Checked override
};

// In one linked .cpp file
Derived::~Derived() = default;

void Derived::action(int value) {
    // Implementation
}
  • A class with virtual functions is polymorphic.
  • A vtable error is usually a linker error, not a constructor logic error.
  • Check every declared virtual function for a matching definition.
  • Check virtual destructors first.
  • Make sure the .cpp file containing the definition is built and linked.
  • Use override for derived virtual functions.
  • A virtual base destructor is needed when deleting derived objects through base pointers.
  • Direct base classes are initialized in constructor initializer lists; indirect bases are not.

FAQ

What does “undefined reference to vtable” mean in C++?

It usually means the linker cannot find a required definition associated with a class that has virtual functions. A missing virtual destructor definition is a frequent cause.

Why does the error point to the constructor?

Object construction requires setup for virtual dispatch. The constructor may be the first place where the unresolved class machinery is needed, even though the missing function is elsewhere.

Does every virtual function need a body?

Every non-pure virtual function declaration needs a matching definition if it is required by the program. A pure virtual function may be left for derived classes to implement, although pure virtual destructors still require a definition.

Is an empty virtual destructor enough?

Yes. Either of these is valid:

CGameModule::~CGameModule() {}
CGameModule::~CGameModule() = default;

Why use override instead of virtual in a derived class?

override tells the compiler that the function must override a base virtual function. It catches accidental signature mismatches. The function remains virtual automatically.

Can this happen if the function definition exists?

Yes. The source file may not be part of the build target, the function signature may differ from the declaration, or the required library may not be linked.

Should a base class destructor be virtual?

Yes, if code might delete a derived object through a base-class pointer:

Base* value = new Derived();
delete value;

Without a virtual base destructor, that deletion has undefined behavior.

Take Quiz

Mini Project

Description

Build a small event-dispatching system with a polymorphic base class and two concrete modules. The project demonstrates correct virtual destructor definitions, safe cleanup through a base pointer, and overriding event handlers.

Goal

Create modules that handle events through a shared base-class interface without vtable linker errors.

Requirements

  1. Create a polymorphic base class named Module with a virtual destructor and a virtual handleEvent function.
  2. Define the base destructor outside the class definition.
  3. Create LoggingModule and CounterModule derived classes.
  4. Override handleEvent in both derived classes using override.
  5. Store derived objects in a collection of std::unique_ptr<Module> and dispatch one event to each.
Take Quiz

Keep learning

Related questions

View more