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.
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:
Animalhas virtual functions, so it is polymorphic.- Creating
animalrequires the program to support its virtual-function machinery. Animal::speak()has a definition, so that entry is available.Animal::~Animal()is declared, but no definition exists.- During linking, the linker tries to resolve all required function symbols and the class’s virtual-function support.
- It cannot find
Animal::~Animal(), and an error may be reported as an undefined destructor or an undefined reference tovtable 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, orComponentclass 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.
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
overridefor derived implementations. - Make base destructors virtual when objects may be deleted through base pointers.
- Ensure every
.cppcontaining 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
= defaultwhen 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
| Situation | Meaning | Typical action |
|---|---|---|
undefined reference to vtable for X | A 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 error | A derived function signature does not match the base virtual function. | Add override and correct the signature. |
| Pure virtual function error when constructing | Code tries to instantiate an abstract class. | Instantiate a concrete derived class that implements required functions. |
| Destructor form | When to use it |
|---|---|
virtual ~Base() = default; in the class | Simple header-only or inline definition. |
virtual ~Base(); plus Base::~Base() = default; in a .cpp file | Common for non-header-only classes and library boundaries. |
| Non-virtual destructor | Only safe when objects will never be deleted through a base pointer. |
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
.cppfile containing the definition is built and linked. - Use
overridefor 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.
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
- Create a polymorphic base class named
Modulewith a virtual destructor and a virtualhandleEventfunction. - Define the base destructor outside the class definition.
- Create
LoggingModuleandCounterModulederived classes. - Override
handleEventin both derived classes usingoverride. - Store derived objects in a collection of
std::unique_ptr<Module>and dispatch one event to each.
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.