undefined reference to vtable

Das Problem

Die Vererbungslehre von C++ führt in einem Fall zur Vererbungsleere und damit beim GNU C++ Compiler g++ zu einer eindeutig unverständlichen Fehlermeldung des Linkers. So erzählte mir mein C-Compiler zur Begründung des abgebrochenen Linkervorgangs folgendes:

bitcube.o:(.gnu.linkonce.r._ZTIN3XSD4View6BitMap7BitCubeE[typeinfo for BitCube]+0x8): undefined reference to `typeinfo for BitCubeMatrix'
bitcube.o: In function `BitCubeMatrix':bitcubematrix.h:9: undefined reference to `vtable for BitCubeMatrix'
bitcubematrix.o: In function `~BitCubeMatrix':bitcubematrix.cpp:9: undefined reference to `vtable for BitCubeMatrix'
:bitcubematrix.cpp:9: undefined reference to `vtable for BitCubeMatrix'
:bitcubematrix.cpp:9: undefined reference to `vtable for BitCubeMatrix'

BitCubeMatrix ist hierbei eine Interface-Klasse zu BitCube. Alle Funktionen von BitCubeMatrix sollten pure virtual deklariert sein. Der Sourcecode wurde dabei fehlerfrei kompiliert, die Fehlermeldungen werden vom Linker ausgegeben, der versucht, die Objektfiles zusammenzufügen. Es liegt also kein erkannter Syntax-Fehler vor.

Der Fehler

Der Fehler, der die oben beschriebene Linkermeldung auslöste, war eine Funktion 'virtual void Updated( void )', die lediglich virtual deklariert war, jedoch nicht pure virtual.

Die Problemlösung

Das Problem tritt (zumindest bei mir bisher) nur in Interface-Klassen auf, also in Klassen, die keine eigene Funktionalität besitzen, sondern lediglich virtuelle Funktionen. Entsprechend existiert zunächst einmal kein cpp-File, da ja auch keine Funktionalität kompiliert werden muss. Die vtable ist eine Tabelle, in der die Adressen aller virtuellen Funktionen gespeichert wird. Hierfür wird eine Variable benötigt, die für den Linker in der entsprechenden Objekt-Datei abgelegt wird. Der Punkt, wo diese Variable eingebaut wird scheint an den Destruktor gekoppelt zu sein. (Ich habe leider den Stroustrup nicht zur Hand, daher 'scheint'…)

  • Obwohl der Destruktor pure virtual deklariert wurde, muss dennoch ein Destruktor definiert werden.
    Damit entsteht zunächst eine .cpp-Datei, in der der Compiler die vtable ablegen kann. Der Versuch eine andere Funktion zu definieren brachte keinen Erfolg, die Fehlermeldung tritt wieder auf. Es muss also wirklich der Destruktor definiert werden.

Schlussfolgerung: Man kann den Destruktur nicht pure virtual definieren, sondern nur virtual. Hierbei wäre eine Fehlermeldung in der Syntaxprüfung hilfreich.

  • Alle Funktionen, die nicht definiert werden, müssen pure virtual deklariert werden.

Das ist eigentlich selbstverständlich, sollte man die Definition für pure virtual allerdings vergessen entsteht beim GCC (4.0.3) die Fehlermeldung nach undefined reference to `vtable for …' aus, die nicht sinnvoll auf das eigentliche Problem hinweist. Jede Funktion, die also nur für ein Interface deklariert wird, aber nicht deklariert wird, muss im Header daher wie folgt als pure virtual gekennzeichnet sein

class Interface
{
  public:
    virtual int interfaceMethod( void ) = 0;
};

Die Problemlösung - The Directors Cut

Nachdem Du nun den Sourcecode korrigiert und 'make' aufgerufen hast, stellst Du fest, dass das Problem immer noch besteht. Alle Sourcecodes, die von der Interface-Klasse abhängen müssen ebenfalls neu kompiliert werden. Lösche also die vorhandenen Objektdateien und rufe dann make erneut auf. Bei einem guten Make-Skript sollte

make clean all

das Problem schnell lösen.

Es hat immernoch nicht geklappt?

Ich erhielt die Fehlermeldung vom Linker ebenfalls, obwohl der Destruktor vorhanden war und auch kompiliert wurde. Also kommentierte ich alles in der Klasse aus bis auf den Destruktor und es kompilierte. So fügte ich alles Schritt für Schritt wieder ein und am Ende ergab es sich, dass eine andere in der Klasse deklarierte Funktion nicht definiert war. Ich hatte die Klasse kopiert, um sie entsprechend abwandeln zu können. Dabei übersah ich, dass eine sehr große Funktion in einer anderen Datei für sich untergebracht war und diese fehlte nun in der neuen Klasse. Die Fehlermeldung war in dem Fall entsprechend irreführend.

Quelle