Undefinierter Verweis auf vtable

357

Beim Erstellen meines C ++ - Programms wird die Fehlermeldung angezeigt

undefinierter Verweis auf 'vtable ...

Was ist die Ursache für dieses Problem? Wie behebe ich das?


Es kommt also vor, dass ich den Fehler für den folgenden Code erhalte (die fragliche Klasse ist CGameModule.) Und ich kann für mein ganzes Leben nicht verstehen, wo das Problem liegt. Zuerst dachte ich, es würde damit zusammenhängen, zu vergessen, einer virtuellen Funktion einen Körper zu geben, aber soweit ich verstehe, ist alles hier. Die Vererbungskette ist etwas lang, aber hier ist der zugehörige Quellcode. Ich bin mir nicht sicher, welche anderen Informationen ich bereitstellen soll.

Hinweis: Im Konstruktor tritt dieser Fehler anscheinend auf.

Mein Code:

class CGameModule : public CDasherModule {
 public:
  CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
  : CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
  { 
      g_pLogger->Log("Inside game module constructor");   
      m_pInterface = pInterface; 
  }

  virtual ~CGameModule() {};

  std::string GetTypedTarget();

  std::string GetUntypedTarget();

  bool DecorateView(CDasherView *pView) {
      //g_pLogger->Log("Decorating the view");
      return false;
  }

  void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }


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

 private:



  CDasherNode *pLastTypedNode;


  CDasherNode *pNextTargetNode;


  std::string m_sTargetString;


  size_t m_stCurrentStringPos;


  CDasherModel *m_pModel;


  CDasherInterfaceBase *m_pInterface;
};

Erbt von ...

class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;

/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
 public:
  CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);

  virtual ModuleID_t GetID();
  virtual void SetID(ModuleID_t);
  virtual int GetType();
  virtual const char *GetName();

  virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
    return false;
  };

 private:
  ModuleID_t m_iID;
  int m_iType;
  const char *m_szName;
};

Welches erbt von ....

namespace Dasher {
  class CEvent;
  class CEventHandler;
  class CDasherComponent;
};

/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
 public:
  CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
  virtual ~CDasherComponent();

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

  bool GetBoolParameter(int iParameter) const;
  void SetBoolParameter(int iParameter, bool bValue) const;

  long GetLongParameter(int iParameter) const;
  void SetLongParameter(int iParameter, long lValue) const;

  std::string GetStringParameter(int iParameter) const;
  void        SetStringParameter(int iParameter, const std::string & sValue) const;

  ParameterType   GetParameterType(int iParameter) const;
  std::string     GetParameterName(int iParameter) const;

 protected:
  Dasher::CEventHandler *m_pEventHandler;
  CSettingsStore *m_pSettingsStore;
};
/// @}


#endif
RyanG
quelle
Welche Funktion wirft den "undefinierten Verweis auf vtable ..."?
J. Polfer
3
Ich habe total vermisst, dass die Fehlermeldung eine Funktion angibt. Es ist zufällig der Konstruktor, also habe ich meinen Klassennamen gesehen und die Verbindung nicht hergestellt. Der Konstruktor wirft dies also. Ich werde dieses Detail zu meinem ursprünglichen Beitrag hinzufügen.
RyanG
3
Wenn Sie Ihre Projektdateien nach wesentlichen Änderungen (z. B. qmake -projectund dann qmake) nicht neu erstellt haben, um eine neue zu generieren Makefile, ist dies eine wahrscheinliche Fehlerquelle bei der Verwendung von Qt.
David C. Rankin
@ DavidC.Rankin, ein weiteres Qt-Problem ist, dass wenn die Datei mit Q_OBJECTextern kopiert wird, aber noch nicht Teil der .pro-Datei ist, sie zwar gut kompiliert wird, aber keine Verknüpfung besteht. Wir müssen diese .h/.cppDatei in die .pro-Datei einfügen, um dies zu können qmake.
iammilind

Antworten:

420

Die GCC-FAQ enthält einen Eintrag:

Die Lösung besteht darin, sicherzustellen, dass alle virtuellen Methoden definiert werden, die nicht rein sind. Beachten Sie, dass ein Destruktor definiert werden muss, auch wenn er als rein virtuell [class.dtor] / 7 deklariert ist.

Alexandre Hamez
quelle
17
nm -C CGameModule.o | grep CGameModule::listet die definierten Methoden auf, vorausgesetzt, Ihre gesamte Klassenimplementierung wird in die logische Objektdatei aufgenommen. Sie können dies mit dem vergleichen, was als virtuell definiert ist, um herauszufinden, was Sie verpasst haben.
Troy Daniels
132
FFS, warum prüft der Compiler das nicht und druckt eine Fehlermeldung aus?
Lenar Hoyt
20
Dies kann natürlich nur vom Linker erkannt werden, nicht vom Compiler.
Xoph
2
In meinem Fall hatten wir eine abstrakte Klasse ohne Destruktorimplementierung. Ich musste die leere Implementierung setzen ~ MyClass () {}
Shefy Gur-ary
1
Sie können eine solche Fehlermeldung erhalten, wenn Objekte, die Sie verknüpfen möchten, im Archiv fehlen (libxyz.a-Datei): undefinierter Verweis auf `vtable for objfilename '
Kemin Zhou
162

Für das, was es wert ist, erzeugt das Vergessen eines Körpers auf einem virtuellen Destruktor Folgendes:

undefinierter Verweis auf "vtable for CYourClass".

Ich füge eine Notiz hinzu, weil die Fehlermeldung täuscht. (Dies war mit gcc Version 4.6.3.)

Dan
quelle
23
Ich musste den Text meines leeren virtuellen Destruktors explizit in die Definitionsdatei (* .cc) einfügen. Es in der Kopfzeile zu haben, gab mir immer noch den Fehler.
PopcornKing
4
Beachten Sie, dass gcc mir nach dem Hinzufügen des virtuellen Destruktors zur Implementierungsdatei den tatsächlichen Fehler mitteilte, bei dem es sich um einen fehlenden Text in einer anderen Funktion handelte.
Moodboom
1
@ PopcornKing Ich sah das gleiche Problem. Selbst das Definieren ~Destructor = default;in der Header-Datei hat nicht geholfen. Gibt es einen dokumentierten Fehler gegen gcc?
RD
Dies mag ein anderes Problem sein, aber mein Problem bestand darin, keine Implementierung für einen nicht virtuellen Destruktor zu haben (wechselte zu eindeutigen / gemeinsam genutzten Zeigern und entfernte sie aus der Quelldatei, hatte aber keine "Implementierung" im Header )
svenevs
Dies löste mein Problem und das Hinzufügen eines leeren {} Körpers für den virtuellen Destruktor verhinderte den Fehler.
Bogdan Ionitza
56

Also habe ich das Problem herausgefunden und es war eine Kombination aus schlechter Logik und nicht ganz vertraut mit der Welt der Automake / Autotools. Ich habe meiner Makefile.am-Vorlage die richtigen Dateien hinzugefügt, war mir aber nicht sicher, in welchem ​​Schritt unseres Erstellungsprozesses das Makefile selbst erstellt wurde. Also habe ich mit einem alten Makefile kompiliert, das überhaupt keine Ahnung von meinen neuen Dateien hatte.

Vielen Dank für die Antworten und den Link zu den GCC-FAQ. Ich werde das sicher lesen, um zu vermeiden, dass dieses Problem aus einem echten Grund auftritt.

RyanG
quelle
43
Kurz gesagt: Die .cpp war einfach nicht im Build enthalten. Die Fehlermeldung ist wirklich irreführend.
Offirmo
67
Für Qt-Benutzer: Sie können denselben Fehler erhalten, wenn Sie vergessen, einen Header zuzuordnen.
Chris Morlier
8
Ich denke, Sie sollten die Antwort von Alexandre Hamez akzeptieren. Personen, die nach diesem Fehler suchen, benötigen höchstwahrscheinlich seine Lösung anstelle Ihrer.
Tim
13
-1 Dies ist möglicherweise die Lösung für Ihr Problem, aber keine Antwort auf die ursprüngliche Frage. Die richtige Antwort ist einfach, dass Sie keine Objektdatei mit den erforderlichen Symbolen bereitgestellt haben. Warum Sie sie nicht zur Verfügung gestellt haben, ist eine andere Geschichte.
Walter
12
@Walter: Eigentlich war das genau die Antwort, nach der ich gesucht habe. Die anderen sind offensichtlich und daher nicht hilfreich.
Edgar Bonet
50

Wenn Sie Qt verwenden, versuchen Sie, qmake erneut auszuführen. Wenn sich dieser Fehler in der Klasse des Widgets befindet, hat qmake möglicherweise nicht bemerkt, dass die vtable der UI-Klasse neu generiert werden sollte. Dies hat das Problem für mich behoben.

gluk47
quelle
2
Ich habe gerade den ganzen Ordner gelöscht, mit Build hat es auch funktioniert.
Tomáš Zato - Wiedereinsetzung Monica
Das ist nicht einzigartig qmake, ich hatte das gleiche mit cmake. Ein Teil des Problems könnte sein, dass beide Tools ein Problem mit Header-Dateien haben, das bei Bedarf möglicherweise nicht immer eine Neuerstellung auslöst.
MSalters
2
Dachte "Rebuild" reran qmake automatisch ... anscheinend nicht. Ich habe "Run qmake" auf Ihren Vorschlag hin ausgeführt, dann "Rebuild" und es hat mein Problem behoben.
Yano
45

Ein undefinierter Verweis auf vtable kann auch aufgrund der folgenden Situation auftreten. Versuchen Sie einfach Folgendes:

Klasse A enthält:

virtual void functionA(parameters)=0; 
virtual void functionB(parameters);

Klasse B enthält:

  1. Die Definition für die obige FunktionA.
  2. Die Definition für die obige FunktionB.

Klasse C enthält: Jetzt schreiben Sie eine Klasse C, in der Sie sie aus Klasse A ableiten werden.

Wenn Sie nun versuchen zu kompilieren, erhalten Sie als Fehler einen undefinierten Verweis auf vtable für Klasse C.

Grund:

functionAwird als rein virtuell definiert und seine Definition wird in Klasse B bereitgestellt. functionBwird als virtuell definiert (NICHT PURE VIRTUAL), sodass versucht wird, seine Definition in Klasse A selbst zu finden, aber Sie haben seine Definition in Klasse B angegeben.

Lösung:

  1. Machen Sie Funktion B als rein virtuell (wenn Sie solche Anforderungen haben) virtual void functionB(parameters) =0; (Dies funktioniert, es wird getestet)
  2. Geben Sie die Definition für Funktion B in Klasse A selbst an und behalten Sie sie als virtuell bei. (Hoffe es funktioniert, da ich das nicht ausprobiert habe)
Srinivasa Lakkaraju
quelle
@ ilya1725 Ihre vorgeschlagene Bearbeitung korrigiert nicht nur die Formatierung und dergleichen, Sie ändern auch die Antwort, indem Sie beispielsweise sagen, dass Klasse C von B anstelle von A abgeleitet ist, und Sie ändern die zweite Lösung. Dies ändert die Antwort erheblich. In diesen Fällen hinterlassen Sie stattdessen bitte einen Kommentar beim Autor. Vielen Dank!
Fabio sagt Reinstate Monica
@FabioTurati Welche Klasse erbt classC von dann? Der Satz ist nicht klar. Was bedeutet "Klasse C enthält:"?
ilya1725
@ ilya1725 Diese Antwort ist nicht sehr klar und ich bin nicht dagegen, sie zu bearbeiten und zu verbessern. Ich sage, dass Ihre Bearbeitung die Bedeutung der Antwort ändert, und das ist eine zu drastische Änderung. Hoffentlich wird der Autor eingreifen und klarstellen, was er meinte (obwohl er schon lange inaktiv war).
Fabio sagt Reinstate Monica
Vielen Dank! In meinem Fall hatte ich nur 2 Klassen in meiner Hierarchie. Klasse A deklarierte eine reine virtuelle Methode. In der Erklärung der Klasse B wurde angegeben, dass diese Methode überschrieben werden würde, aber ich hatte die Definition der überschriebenen Methode noch nicht geschrieben.
Nick Desaulniers
44

Ich habe diesen Fehler einfach erhalten, weil meine CPP-Datei nicht im Makefile war.

Wille
quelle
In der Tat scheint sich die Botschaft geringfügig von der üblichen zu ändern, undefined reference to {function/class/struct}wenn es um virtualDinge geht. Warf mich ab.
Keith M
30

Was ist ein vtable?

Es kann hilfreich sein, zu wissen, worüber die Fehlermeldung spricht, bevor Sie versuchen, sie zu beheben. Ich werde auf einem hohen Niveau beginnen und dann auf einige weitere Details eingehen. Auf diese Weise können Benutzer überspringen, sobald sie mit dem Verständnis von vtables vertraut sind. … Und da springen gerade eine Menge Leute voran. :) Für diejenigen, die hier bleiben:

Eine vtable ist im Grunde die häufigste Implementierung von Polymorphismus in C ++ . Wenn vtables verwendet werden, hat jede polymorphe Klasse irgendwo im Programm eine vtable. Sie können es sich als (verstecktes) staticDatenelement der Klasse vorstellen. Jedes Objekt einer polymorphen Klasse ist der vtable für die am meisten abgeleitete Klasse zugeordnet. Durch Überprüfen dieser Zuordnung kann das Programm seine polymorphe Magie entfalten. Wichtige Einschränkung: Eine vtable ist ein Implementierungsdetail. Es wird vom C ++ - Standard nicht vorgeschrieben, obwohl die meisten (alle?) C ++ - Compiler vtables verwenden, um polymorphes Verhalten zu implementieren. Die Details, die ich präsentiere, sind entweder typische oder vernünftige Ansätze. Compiler dürfen davon abweichen!

Jedes polymorphe Objekt hat einen (versteckten) Zeiger auf die vtable für die am meisten abgeleitete Klasse des Objekts (in den komplexeren Fällen möglicherweise mehrere Zeiger). Durch Betrachten des Zeigers kann das Programm den "echten" Typ eines Objekts erkennen (außer während der Konstruktion, aber lassen Sie uns diesen Sonderfall überspringen). Wenn beispielsweise ein Objekt vom Typ Anicht auf die vtable von verweist A, ist dieses Objekt tatsächlich ein Unterobjekt von etwas, von dem abgeleitet wurde A.

Der Name „VTable“ kommt von „ v irtual Funktion Tabelle “. In dieser Tabelle werden Zeiger auf (virtuelle) Funktionen gespeichert. Ein Compiler wählt seine Konvention für die Anordnung der Tabelle. Ein einfacher Ansatz besteht darin, die virtuellen Funktionen in der Reihenfolge zu durchlaufen, in der sie in den Klassendefinitionen deklariert sind. Wenn eine virtuelle Funktion aufgerufen wird, folgt das Programm dem Zeiger des Objekts auf eine vtable, geht zu dem Eintrag, der der gewünschten Funktion zugeordnet ist, und verwendet dann den gespeicherten Funktionszeiger, um die richtige Funktion aufzurufen. Es gibt verschiedene Tricks, um diese Arbeit zu machen, aber ich werde hier nicht darauf eingehen.

Wo / wann wird ein vtablegeneriert?

Eine vtable wird vom Compiler automatisch generiert (manchmal als "emittiert" bezeichnet). Ein Compiler könnte in jeder Übersetzungseinheit, die eine polymorphe Klassendefinition sieht, eine vtable ausgeben, aber das wäre normalerweise ein unnötiger Overkill. Eine Alternative ( von gcc und wahrscheinlich von anderen verwendet) besteht darin, eine einzelne Übersetzungseinheit auszuwählen, in der die vtable platziert werden soll, ähnlich wie Sie eine einzelne Quelldatei auswählen würden, in die die statischen Datenelemente einer Klasse eingefügt werden sollen. Wenn bei diesem Auswahlprozess keine Übersetzungseinheiten ausgewählt werden, wird die vtable zu einer undefinierten Referenz. Daher der Fehler, dessen Botschaft zugegebenermaßen nicht besonders klar ist.

In ähnlicher Weise wird die vtable zu einer undefinierten Referenz, wenn der Auswahlprozess eine Übersetzungseinheit auswählt, diese Objektdatei jedoch nicht für den Linker bereitgestellt wird. Leider kann die Fehlermeldung in diesem Fall noch weniger deutlich sein als in dem Fall, in dem der Auswahlprozess fehlgeschlagen ist. (Vielen Dank an die Antwortenden, die diese Möglichkeit erwähnt haben. Ich hätte es sonst wahrscheinlich vergessen.)

Der von gcc verwendete Auswahlprozess ist sinnvoll, wenn wir mit der Tradition beginnen, jeder Klasse, die für ihre Implementierung eine benötigt, eine (einzelne) Quelldatei zuzuweisen. Es wäre schön, die vtable beim Kompilieren dieser Quelldatei auszugeben. Nennen wir das unser Ziel. Der Auswahlprozess muss jedoch funktionieren, auch wenn diese Tradition nicht befolgt wird. Anstatt nach der Implementierung der gesamten Klasse zu suchen, suchen wir nach der Implementierung eines bestimmten Mitglieds der Klasse. Wenn der Tradition gefolgt wird - und wenn dieses Mitglied tatsächlich umgesetzt wird - dann erreicht dies das Ziel.

Das von gcc (und möglicherweise von anderen Compilern) ausgewählte Mitglied ist die erste nicht inline virtuelle Funktion, die nicht rein virtuell ist. Wenn Sie Teil der Menge sind, die Konstruktoren und Destruktoren vor anderen Mitgliedsfunktionen deklariert, hat dieser Destruktor gute Chancen, ausgewählt zu werden. (Sie haben daran gedacht, den Destruktor virtuell zu machen, oder?) Es gibt Ausnahmen. Ich würde erwarten, dass die häufigsten Ausnahmen sind, wenn eine Inline-Definition für den Destruktor bereitgestellt wird und wenn der Standard-Destruktor angefordert wird (mit " = default").

Der Kluge könnte bemerken, dass eine polymorphe Klasse Inline-Definitionen für alle ihre virtuellen Funktionen bereitstellen darf. Führt das nicht dazu, dass der Auswahlprozess fehlschlägt? Dies ist bei älteren Compilern der Fall. Ich habe gelesen, dass die neuesten Compiler diese Situation angegangen sind, kenne aber keine relevanten Versionsnummern. Ich könnte versuchen, dies nachzuschlagen, aber es ist einfacher, entweder Code zu verwenden oder darauf zu warten, dass sich der Compiler beschwert.

Zusammenfassend gibt es drei Hauptursachen für den Fehler "undefinierter Verweis auf vtable":

  1. Einer Mitgliedsfunktion fehlt ihre Definition.
  2. Eine Objektdatei wird nicht verknüpft.
  3. Alle virtuellen Funktionen haben Inline-Definitionen.

Diese Ursachen allein reichen nicht aus, um den Fehler selbst zu verursachen. Diese würden Sie vielmehr ansprechen, um den Fehler zu beheben. Erwarten Sie nicht, dass das absichtliche Erstellen einer dieser Situationen diesen Fehler definitiv hervorruft. Es gibt andere Anforderungen. Erwarten Sie, dass das Beheben dieser Situationen diesen Fehler behebt.

(OK, Nummer 3 könnte ausreichend gewesen sein, als diese Frage gestellt wurde.)

Wie behebe ich den Fehler?

Willkommen zurück, Leute, die vorausspringen! :) :)

  1. Sehen Sie sich Ihre Klassendefinition an. Suchen Sie die erste virtuelle Nicht-Inline-Funktion, die nicht rein virtuell ist (nicht " = 0") und deren Definition Sie angeben (nicht " = default").
    • Wenn es keine solche Funktion gibt, versuchen Sie, Ihre Klasse so zu ändern, dass es eine gibt. (Fehler möglicherweise behoben.)
    • Siehe auch die Antwort von Philip Thomas für eine Einschränkung.
  2. Suchen Sie die Definition für diese Funktion. Wenn es fehlt, fügen Sie es hinzu! (Fehler möglicherweise behoben.)
  3. Überprüfen Sie Ihren Link-Befehl. Wenn die Objektdatei mit der Definition dieser Funktion nicht erwähnt wird, beheben Sie das! (Fehler möglicherweise behoben.)
  4. Wiederholen Sie die Schritte 2 und 3 für jede virtuelle Funktion und dann für jede nicht virtuelle Funktion, bis der Fehler behoben ist. Wenn Sie immer noch nicht weiterkommen, wiederholen Sie dies für jedes statische Datenelement.

Beispiel
Die Details der zu erledigenden Aufgaben können variieren und manchmal in separate Fragen verzweigen (z. B. Was ist ein undefinierter Verweis / ungelöster externer Symbolfehler und wie behebe ich ihn? ). Ich werde jedoch ein Beispiel dafür geben, was in einem bestimmten Fall zu tun ist, das neuere Programmierer verwirren könnte.

In Schritt 1 wird erwähnt, dass Ihre Klasse so geändert wird, dass sie eine Funktion eines bestimmten Typs hat. Wenn die Beschreibung dieser Funktion über Ihren Kopf ging, könnten Sie sich in der Situation befinden, die ich ansprechen möchte. Denken Sie daran, dass dies ein Weg ist, um das Ziel zu erreichen. Dies ist nicht der einzige Weg, und es könnte leicht bessere Wege in Ihrer spezifischen Situation geben. Rufen wir Ihre Klasse an A. Ist Ihr Destruktor (in Ihrer Klassendefinition) als einer der beiden deklariert?

virtual ~A() = default;

oder

virtual ~A() {}

? In diesem Fall ändern zwei Schritte Ihren Destruktor in den gewünschten Funktionstyp. Ändern Sie zuerst diese Zeile in

virtual ~A();

Fügen Sie zweitens die folgende Zeile in eine Quelldatei ein, die Teil Ihres Projekts ist (vorzugsweise die Datei mit der Klassenimplementierung, falls vorhanden):

A::~A() {}

Dadurch ist Ihr (virtueller) Destruktor nicht inline und wird nicht vom Compiler generiert. (Sie können jederzeit Änderungen an Ihrem Code-Formatierungsstil vornehmen, z. B. einen Header-Kommentar zur Funktionsdefinition hinzufügen.)

JaMiT
quelle
Oh, Bravo! Für die sehr detaillierte und sehr gut durchdachte Erklärung.
David C. Rankin
Musste viel zu weit nach unten scrollen, um dies zu lesen. Haben Sie eine positive Bewertung für die hervorragende Erklärung!
Thomas
24

In verschiedenen Antworten wird viel spekuliert. Ich werde im Folgenden einen ziemlich minimalen Code angeben, der diesen Fehler reproduziert, und erklären, warum er auftritt.

Ziemlich minimaler Code, um diesen Fehler zu reproduzieren

IBase.hpp

#pragma once

class IBase {
    public:
        virtual void action() = 0;
};

Derived.hpp

#pragma once

#include "IBase.hpp"

class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

Derived.cpp

#include "Derived.hpp"
Derived::Derived(int a) { }
void Derived::action() {}

myclass.cpp

#include <memory>
#include "Derived.hpp"

class MyClass {

    public:
        MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) {

        }

        void doSomething() {
            instance->action();
        }

    private:
        std::shared_ptr<Derived> instance;
};

int main(int argc, char** argv) {
    Derived myInstance(5);
    MyClass c(std::make_shared<Derived>(myInstance));
    c.doSomething();
    return 0;
}

Sie können dies mit GCC wie folgt kompilieren:

g++ -std=c++11 -o a.out myclass.cpp Derived.cpp

Sie können den Fehler jetzt reproduzieren, indem Sie ihn = 0in IBase.hpp entfernen . Ich erhalte diesen Fehler:

~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp
/tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)':
myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o: In function `IBase::IBase()':
Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase'
collect2: error: ld returned 1 exit status

Erläuterung

Beachten Sie, dass für den obigen Code keine virtuellen Destruktoren, Konstruktoren oder andere zusätzliche Dateien erforderlich sind, damit die Kompilierung erfolgreich ist (obwohl Sie sie haben sollten).

Um diesen Fehler zu verstehen, gehen Sie wie folgt vor: Linker sucht nach einem Konstruktor für IBase. Dies wird es für den Konstruktor von Derived benötigen. Da Derived jedoch Methoden aus IBase überschreibt, ist eine vtable angehängt, die auf IBase verweist. Wenn der Linker "undefinierter Verweis auf vtable für IBase" sagt, bedeutet dies im Grunde, dass Derived einen vtable-Verweis auf IBase hat, aber keinen kompilierten Objektcode von IBase finden kann, zu dem nachgeschlagen werden kann. Unter dem Strich hat die Klasse IBase Deklarationen ohne Implementierungen. Dies bedeutet, dass eine Methode in IBase als virtuell deklariert ist, wir jedoch vergessen haben, sie als rein virtuell zu markieren ODER ihre Definition anzugeben.

Abschiedstipp

Wenn alles andere fehlschlägt, besteht eine Möglichkeit, diesen Fehler zu beheben, darin, ein minimales Programm zu erstellen, das kompiliert wird, und es dann ständig zu ändern, damit es den gewünschten Status erreicht. Kompilieren Sie zwischendurch weiter, um zu sehen, wann es fehlschlägt.

Hinweis zum ROS- und Catkin-Build-System

Wenn Sie die oben genannten Klassen in ROS mit dem Catkin-Build-System kompiliert haben, benötigen Sie die folgenden Zeilen in CMakeLists.txt:

add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})

Die erste Zeile besagt im Grunde, dass wir eine ausführbare Datei mit dem Namen myclass erstellen möchten, und der Code zum Erstellen dieser Dateien enthält die folgenden Dateien. Eine dieser Dateien sollte main () haben. Beachten Sie, dass Sie in CMakeLists.txt keine .hpp-Dateien angeben müssen. Außerdem müssen Sie Derived.cpp nicht als Bibliothek angeben.

Shital Shah
quelle
18

Ich bin gerade auf eine andere Ursache für diesen Fehler gestoßen, auf die Sie prüfen können.

Die Basisklasse definierte eine reine virtuelle Funktion als:

virtual int foo(int x = 0);

Und die Unterklasse hatte

int foo(int x) override;

Das Problem war der Tippfehler, dass der "=0"außerhalb der Klammer stehen sollte:

virtual int foo(int x) = 0;

Wenn Sie also so weit nach unten scrollen, haben Sie wahrscheinlich keine Antwort gefunden - dies ist etwas anderes, auf das Sie achten müssen.

Philip Thomas
quelle
12

Dies kann sehr leicht passieren, wenn Sie vergessen, eine Verknüpfung zu der Objektdatei mit der Definition herzustellen.

Hazok
quelle
1
Bitte fügen Sie Ihrer Antwort eine weitere Beschreibung und eine mögliche Lösung hinzu.
Mohit Jain
1
Das Vergessen des Verlinkens kann das Vergessen des Hinzufügens zum Erstellen von Anweisungen beinhalten. In meinem Fall hatte meine CPP-Datei alles perfekt 'definiert', außer dass ich vergessen habe, die CPP-Datei zur Quellliste hinzuzufügen (in meiner CMakeLists.txt, aber das gleiche kann in anderen Build-Systemen wie in einer .pro-Datei passieren). Infolgedessen wurde alles kompiliert und dann bekam ich den Fehler zur Linkzeit ...
Salbei
@Mohit Jain Wie die Objektdateien verlinkt werden, hängt von der Einrichtung und den Werkzeugen der Umgebung ab. Leider kann ein spezifischer Fix für eine Person für eine andere Person unterschiedlich sein (z. B. CMake gegen proprietäres Tool gegen IDE gegen usw.)
Hazok
11

Der GNU C ++ - Compiler muss entscheiden, wo vtabledie Definition der virtuellen Funktionen eines Objekts abgelegt werden soll, falls Sie die Definition der virtuellen Funktionen eines Objekts auf mehrere Kompilierungseinheiten verteilt haben (z. B. befinden sich einige der Definitionen der virtuellen Funktionen des Objekts in einer CPP-Datei, andere in einer anderen. CPP-Datei usw.).

Der Compiler legt die vtablePosition an der Stelle ab, an der die erste deklarierte virtuelle Funktion definiert ist.

Wenn Sie aus irgendeinem Grund vergessen haben, eine Definition für diese erste im Objekt deklarierte virtuelle Funktion anzugeben (oder fälschlicherweise vergessen haben, das kompilierte Objekt in der Verknüpfungsphase hinzuzufügen), wird dieser Fehler angezeigt.

Beachten Sie als Nebeneffekt, dass Sie nur für diese bestimmte virtuelle Funktion nicht den herkömmlichen Linker-Fehler erhalten, da Ihnen die Funktion foo fehlt .

Iulian Popa
quelle
8

Nicht um aber zu überqueren. Wenn Sie sich mit Vererbung befassen, war der zweite Google-Treffer das, was ich verpasst hatte, dh. Alle virtuellen Methoden sollten definiert werden.

Sowie:

virtual void fooBar() = 0;

Weitere Informationen finden Sie unter Answare C ++ Undefined Referenz zu vtable und Vererbung . Ich habe gerade festgestellt, dass es bereits oben erwähnt wurde, aber es könnte jemandem helfen.

Victor Häggqvist
quelle
8

Ok, die Lösung hierfür ist, dass Sie möglicherweise die Definition verpasst haben. Sehen Sie sich das folgende Beispiel an, um den vtable-Compilerfehler zu vermeiden:

// In the CGameModule.h

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

    virtual void init();
};

// In the CGameModule.cpp

#include "CGameModule.h"

CGameModule::CGameModule()
{

}

CGameModule::~CGameModule()
{

}

void CGameModule::init()    // Add the definition
{

}
Sammy
quelle
7
  • Bist du sicher, dass CDasherComponentes einen Körper für den Destruktor gibt? Es ist definitiv nicht hier - die Frage ist, ob es in der .cc-Datei ist.
  • Aus Stilperspektive CDasherModulesollte der Destruktor explizit definiert werden virtual.
  • Es sieht so aus, CGameModuleals hätte es } am Ende (nach dem }; // for the class) ein Extra .
  • Wird CGameModulemit den Bibliotheken verknüpft, die CDasherModuleund definieren CDasherComponent?
Stephen
quelle
- Ja, CDasherComponent hat einen Destruktorkörper in der CPP. Ich dachte, es wurde in der .h deklariert, als ich dies gepostet habe. - Zur Kenntnis genommen. - Das war eine zusätzliche Klammer, die ich versehentlich beim Entfernen der Dokumentation hinzugefügt habe. - Soweit ich weiß, ja. Ich habe eine Automake-Datei geändert, die ich nicht geschrieben habe, aber ich habe die Muster befolgt, die für andere Klassen mit demselben Vererbungsmuster aus denselben Klassen funktioniert haben, es sei denn, ich habe einen dummen Fehler gemacht (völlig möglich). Ich glaube nicht, dass es das ist.
RyanG
@RyanG: Versuchen Sie, alle virtuellen Funktionsdefinitionen in die Klassendefinition zu verschieben. Stellen Sie sicher, dass alle vorhanden sind, und prüfen Sie, ob sich das Ergebnis ändert.
Stephen
5

Vielleicht trägt das Fehlen des virtuellen Destruktors dazu bei?

virtual ~CDasherModule(){};
DevByStarlight
quelle
5

Dies war das erste Suchergebnis für mich, daher dachte ich, ich würde noch etwas hinzufügen, um zu überprüfen: Stellen Sie sicher, dass die Definition der virtuellen Funktionen tatsächlich in der Klasse enthalten ist. In meinem Fall hatte ich Folgendes:

Header-Datei:

class A {
 public:
  virtual void foo() = 0;
};

class B : public A {
 public:
  void foo() override;
};

und in meiner .cc-Datei:

void foo() {
  ...
}

Dies sollte lesen

void B::foo() {
}
RedSpikeyThing
quelle
3

Nicht vielleicht. Auf jeden Fall ~CDasherModule() {}fehlt.

Bretzelus
quelle
3

So viele Antworten hier, aber keine von ihnen schien mein Problem abgedeckt zu haben. Ich hatte folgendes:


class I {
    virtual void Foo()=0;
};

Und in einer anderen Datei (natürlich in der Zusammenstellung und Verknüpfung enthalten)

class C : public I{
    void Foo() {
        //bar
    }
};

Nun, das hat nicht funktioniert und ich habe den Fehler bekommen, über den alle reden. Um es zu lösen, musste ich die eigentliche Definition von Foo aus der Klassendeklaration als solche entfernen:

class C : public I{
    void Foo();
};

C::Foo(){
   //bar
}

Ich bin kein C ++ - Guru, daher kann ich nicht erklären, warum dies korrekter ist, aber es hat das Problem für mich gelöst.

Nathan Daniels
quelle
Ich bin kein C ++ - Guru, aber es scheint damit zu tun zu haben, die Deklaration und Definition in derselben Datei mit einer zusätzlichen Definitionsdatei zu mischen.
Terry G Lorber
1
Wenn sich die Funktionsdefinition in Ihrer Klassendefinition befand, wurde sie implizit als "inline" deklariert. Zu diesem Zeitpunkt waren alle Ihre virtuellen Funktionen integriert. Wenn Sie die Funktion außerhalb der Klassendefinition verschoben haben, war sie keine "Inline" -Funktion mehr. Da Sie eine nicht inline integrierte virtuelle Funktion hatten, wusste Ihr Compiler, wo die vtable ausgegeben werden musste. (Eine gründlichere Erklärung passt nicht in einen Kommentar.)
JaMiT
3

Also habe ich Qt mit Windows XP und MinGW Compiler verwendet und dieses Ding hat mich verrückt gemacht.

Grundsätzlich wurde die Datei moc_xxx.cpp leer generiert, auch wenn ich hinzugefügt wurde

Q_OBJECT

Das Löschen von Funktionen, die virtuell, explizit und was auch immer Sie vermuten, hat nicht funktioniert. Schließlich fing ich an, Zeile für Zeile zu entfernen, und es stellte sich heraus, dass ich hatte

#ifdef something

Um die Datei. Auch wenn das #ifdef wahr war, wurde keine MOC-Datei generiert.

Das Entfernen aller #ifdefs hat das Problem behoben.

Dies geschah nicht mit Windows und VS 2013.

Daniel Georgiev
quelle
Durch das Auskommentieren der Zeile Q_OBJECT wurde meine einfache Test-App mit einer Ebene erstellt g++ *.cpp .... (Benötigte etwas schnelles und schmutziges, aber qmake war voller Trauer.)
Nathan Kidd
2

Wenn alles andere fehlschlägt, suchen Sie nach Duplikaten. Ich wurde durch den expliziten anfänglichen Verweis auf Konstruktoren und Destruktoren fehlgeleitet, bis ich einen Verweis in einem anderen Beitrag las. Es ist jede ungelöste Methode. In meinem Fall dachte ich, ich hätte die Deklaration, die char * xml als Parameter verwendete, durch eine Deklaration ersetzt, die die unnötig störende const char * xml verwendet, aber stattdessen hatte ich eine neue erstellt und die andere an Ort und Stelle belassen.

Jerry Miller
quelle
2

Es gibt viele Möglichkeiten, diesen Fehler zu verursachen, und ich bin sicher, dass viele von ihnen den Fehler verursachen. In meinem Fall gab es aufgrund einer Duplizierung der Quelldatei eine andere Definition derselben Klasse. Diese Datei wurde kompiliert, aber nicht verlinkt, sodass sich der Linker darüber beschwerte, dass er sie nicht finden konnte.

Zusammenfassend würde ich sagen, wenn Sie die Klasse lange genug angestarrt haben und nicht sehen können, welches mögliche Syntaxproblem sie verursachen könnte, suchen Sie nach Build-Problemen wie einer fehlenden oder einer duplizierten Datei.

crw4096
quelle
2

In meinem Fall verwende ich Qt und habe eine QObjectUnterklasse in einer foo.cpp(nicht .h) Datei definiert. Das Update sollte #include "foo.moc"am Ende von hinzugefügt werden foo.cpp.

Stefan Monov
quelle
2

Ich denke, es ist auch erwähnenswert, dass Sie die Nachricht auch erhalten, wenn Sie versuchen, eine Verknüpfung zu einem Objekt einer Klasse herzustellen , die mindestens eine virtuelle Methode hat und der Linker die Datei nicht finden kann. Zum Beispiel:

Foo.hpp:

class Foo
{
public:
    virtual void StartFooing();
};

Foo.cpp:

#include "Foo.hpp"

void Foo::StartFooing(){ //fooing }

Zusammengestellt mit:

g++ Foo.cpp -c

Und main.cpp:

#include "Foo.hpp"

int main()
{
    Foo foo;
}

Zusammengestellt und verknüpft mit:

g++ main.cpp -o main

Gibt unseren Lieblingsfehler:

/tmp/cclKnW0g.o: In der Funktion main': main.cpp:(.text+0x1a): undefined reference tovtable für Foo 'collect2: Fehler: ld hat 1 Exit-Status zurückgegeben

Dies ist auf mein Verständnis zurückzuführen, weil:

  1. Vtable wird pro Klasse zur Kompilierungszeit erstellt

  2. Linker hat keinen Zugriff auf vtable in Foo.o.

Adam Stepniak
quelle
1

Ich habe diesen Fehler im folgenden Szenario erhalten

Stellen Sie sich einen Fall vor, in dem Sie die Implementierung von Elementfunktionen einer Klasse in der Header-Datei selbst definiert haben. Diese Header-Datei ist ein exportierter Header (mit anderen Worten, sie wird möglicherweise in eine allgemeine Datei kopiert / direkt in Ihre Codebasis aufgenommen). Jetzt haben Sie beschlossen, die Implementierung der Mitgliedsfunktionen in eine CPP-Datei zu trennen. Nachdem Sie die Implementierung in .cpp getrennt / verschoben haben, enthält die Header-Datei nur noch die Prototypen der Elementfunktionen innerhalb der Klasse. Wenn Sie nach den obigen Änderungen Ihre Codebasis erstellen, wird möglicherweise der Fehler "undefinierter Verweis auf 'vtable ..." angezeigt.

Um dies zu beheben, stellen Sie vor dem Erstellen sicher, dass Sie die Header-Datei (an der Sie Änderungen vorgenommen haben) im Verzeichnis common / include löschen. Stellen Sie außerdem sicher, dass Sie Ihr Makefile ändern, um die neue .o-Datei aufzunehmen / hinzuzufügen, die aus der neuen .cpp-Datei erstellt wurde, die Sie gerade erstellt haben. Wenn Sie diese Schritte ausführen, beschwert sich der Compiler / Linker nicht mehr.

Sidharth Middela
quelle
seltsam. Wenn ein Header an eine andere Stelle kopiert werden soll, sollte das Build-System die Kopie automatisch aktualisieren, sobald das Original geändert wird und bevor sie in eine andere Datei aufgenommen wird. Wenn Sie es manuell tun müssen, werden Sie geschraubt.
Offirmo
1

Ich habe diese Art von Fehler in Situationen erhalten, in denen ich versucht habe, eine Verknüpfung zu einem Objekt herzustellen, als ich einen Make-Fehler bekam, der das Hinzufügen des Objekts zum Archiv verhinderte.

Angenommen, ich habe libXYZ.a, das bioseq.o in int haben soll, aber nicht.

Ich habe eine Fehlermeldung erhalten:

combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'

Dies unterscheidet sich von allen oben genannten. Ich würde dieses fehlende Objekt im Archivproblem nennen.

Kemin Zhou
quelle
0

Es ist auch möglich, dass Sie eine Nachricht wie erhalten

SomeClassToTest.host.o: In function `class1::class1(std::string const&)':
class1.hpp:114: undefined reference to `vtable for class1'
SomeClassToTest.host.o: In function `class1::~class1()':
class1.hpp:119: undefined reference to `vtable for class1'
collect2: error: ld returned 1 exit status
[link] FAILED: 'g++' '-o' 'stage/tests/SomeClassToTest' 'object/tests/SomeClassToTest.host.o' 'object/tests/FakeClass1.SomeClassToTest.host.o'

Wenn Sie vergessen haben, eine virtuelle Funktion einer Klasse FakeClass1 zu definieren, wenn Sie versuchen, einen Komponententest für eine andere Klasse SomeClass zu verknüpfen.

//class declaration in class1.h
class class1
{
    public:
    class1()
    {
    }
    virtual ~class1()
    {
    }
    virtual void ForgottenFunc();
};

Und

//class definition in FakeClass1.h
//...
//void ForgottenFunc() {} is missing here

In diesem Fall schlage ich vor, dass Sie Ihre Fälschung für Klasse 1 noch einmal überprüfen. Sie werden wahrscheinlich feststellen, dass Sie möglicherweise vergessen haben, eine virtuelle Funktion ForgottenFuncin Ihrer gefälschten Klasse zu definieren .

Yuriy
quelle
0

Ich habe diesen Fehler erhalten, als ich einem vorhandenen Quell / Header-Paar eine zweite Klasse hinzugefügt habe. Zwei Klassenheader in derselben .h-Datei und Funktionsdefinitionen für zwei Klassen in derselben .cpp-Datei.

Ich habe das schon einmal erfolgreich gemacht, mit Klassen, die eng zusammenarbeiten sollen, aber anscheinend hat mich diesmal etwas nicht gemocht. Ich weiß immer noch nicht was, aber die Aufteilung in eine Klasse pro Kompilierungseinheit hat das Problem behoben.


Der fehlgeschlagene Versuch:

_gui_icondata.h:

#ifndef ICONDATA_H
#define ICONDATA_H

class Data;
class QPixmap;

class IconData
{
public:
    explicit IconData();
    virtual ~IconData();

    virtual void setData(Data* newData);
    Data* getData() const;
    virtual const QPixmap* getPixmap() const = 0;

    void toggleSelected();
    void toggleMirror();
    virtual void updateSelection() = 0;
    virtual void updatePixmap(const QPixmap* pixmap) = 0;

protected:
    Data* myData;
};

//--------------------------------------------------------------------------------------------------

#include "_gui_icon.h"

class IconWithData : public Icon, public IconData
{
    Q_OBJECT
public:
    explicit IconWithData(QWidget* parent);
    virtual ~IconWithData();

    virtual const QPixmap* getPixmap() const;
    virtual void updateSelection();
    virtual void updatePixmap(const QPixmap* pixmap);

signals:

public slots:
};

#endif // ICONDATA_H

_gui_icondata.cpp:

#include "_gui_icondata.h"

#include "data.h"

IconData::IconData()
{
    myData = 0;
}

IconData::~IconData()
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    //don't need to clean up any more; this entire object is going away anyway
}

void IconData::setData(Data* newData)
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    myData = newData;
    if(myData)
    {
        myData->addIcon(this, false);
    }
    updateSelection();
}

Data* IconData::getData() const
{
    return myData;
}

void IconData::toggleSelected()
{
    if(!myData)
    {
        return;
    }

    myData->setSelected(!myData->getSelected());
    updateSelection();
}

void IconData::toggleMirror()
{
    if(!myData)
    {
        return;
    }

    myData->setMirrored(!myData->getMirrored());
    updateSelection();
}

//--------------------------------------------------------------------------------------------------

IconWithData::IconWithData(QWidget* parent) :
    Icon(parent), IconData()
{
}

IconWithData::~IconWithData()
{
}

const QPixmap* IconWithData::getPixmap() const
{
    return Icon::pixmap();
}

void IconWithData::updateSelection()
{
}

void IconWithData::updatePixmap(const QPixmap* pixmap)
{
    Icon::setPixmap(pixmap, true, true);
}

Wiederum fügte das Hinzufügen eines neuen Quell / Header-Paares und das wörtliche Ausschneiden / Einfügen der IconWithData-Klasse "gerade funktioniert" hinzu.

AaronD
quelle
0

Mein Fall war albern, ich hatte versehentlich ein Extra "danach #includeund weißt du was?

undefined reference to vtable!

Ich habe mich stundenlang am Kopf und am Gesicht gekratzt und virtuelle Funktionen kommentiert, um zu sehen, ob sich etwas geändert hat, und schließlich wurde durch Entfernen des Extra "alles repariert! Diese Art von Dingen muss wirklich zu einem Kompilierungsfehler führen, nicht zu einem Linkfehler.

Mit extra "meine ich:

#include "SomeHeader.h""
Bahram Pouryousefi
quelle
0

In meinem Fall hatte ich eine Basisklasse namens Person und zwei abgeleitete Klassen namens Student und Professor.

Wie mein Programm repariert wurde, war: 1. Ich habe alle Funktionen in der Basisklasse gemacht. Pure Virtual. 2. Ich habe alle virtuellen Destruktoren als verwendetdefault ones.

Gaurav Kumar
quelle
-3

Ich habe diesen Fehler nur erhalten, weil sich der Name eines Konstruktorarguments in der Header-Datei und in der Implementierungsdatei unterschied. Die Konstruktorsignatur lautet

PointSet (const PointSet & pset, Parent * parent = 0);

und was ich in der Implementierung geschrieben habe, begann mit

PointSet (const PointSet & pest, Parent * parent)

daher habe ich versehentlich "pset" durch "pest" ersetzt. Der Compiler beschwerte sich über diesen einen und zwei andere Konstruktoren, bei denen überhaupt kein Fehler auftrat. Ich verwende g ++ Version 4.9.1 unter Ubuntu. Das Definieren eines virtuellen Destruktors in dieser abgeleiteten Klasse machte keinen Unterschied (es wird in der Basisklasse definiert). Ich hätte diesen Fehler nie gefunden, wenn ich die Körper der Konstruktoren nicht in die Header-Datei eingefügt und sie so in der Klasse definiert hätte.

vitke
quelle
7
Das würde überhaupt keinen Unterschied machen, Sie müssen den Fehler woanders gehabt und ihn versehentlich behoben haben.
MM