GCC C ++ Linker-Fehler: Undefinierter Verweis auf 'vtable for XXX', undefinierter Verweis auf 'ClassName :: ClassName ()'

70

Ich richte ein C ++ - Projekt unter Ubuntu x64 mit Eclipse-CDT ein. Ich mache im Grunde genommen eine Hallo-Welt und verbinde mich mit einer kommerziellen Bibliothek von Drittanbietern.

Ich habe die Header-Dateien eingefügt, die mit ihren Bibliotheken verknüpft sind, aber ich erhalte immer noch Linker-Fehler. Gibt es hier andere mögliche Probleme als die offensichtlichen (z. B. bin ich zu 99% sicher, dass ich auf die richtige Bibliothek verlinke).

  1. Gibt es eine Möglichkeit zu bestätigen, dass die statischen Bibliotheken, mit denen ich verknüpfe, 64-Bit sind?
  2. Gibt es eine Möglichkeit zu bestätigen, dass die Bibliothek die Klasse (und Methoden) hat, die ich erwarte?

Eclipse sagt:

Gebäudeziel: LinkProblem
Aufrufen: GCC C ++ Linker
g ++ -L / home / notroot / workspace / somelib-3 / somelib / target / bin -o "LinkProblem" ./src/LinkProblem.o -lsomelib1 -lpthread -lsomelib2 -lsomelib3
./src/LinkProblem.o: In der Funktion `main ':
/home/notroot/workspace/LinkProblem/Debug/../src/LinkProblem.cpp:17: undefinierter Verweis auf `SomeClass :: close () '
./src/LinkProblem.o: In der Funktion `SomeOtherClass ':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefinierter Verweis auf `SomeClass :: SomeClass () '
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefinierter Verweis auf "vtable for SomeOtherClass"
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:151: undefinierter Verweis auf `SomeClass :: ~ SomeClass () '
./src/LinkProblem.o: In der Funktion `~ SomeOtherClass ':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefinierter Verweis auf "vtable for SomeOtherClass"
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefinierter Verweis auf `SomeClass :: ~ SomeClass () '
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefinierter Verweis auf `SomeClass :: ~ SomeClass () '
collect2: ld hat 1 Exit-Status zurückgegeben
make: *** [LinkProblem] Fehler 1
Alex Black
quelle
Ist die Bibliothek eines Drittanbieters 64-Bit?
Daniel A. White
Ja, es ist 64bit. Sie könnten aber auf etwas sein. Wie stelle ich sicher, dass mein Code / Projekt 64-Bit ist? In Visual Studio habe ich eine x64-Build-Konfiguration erstellt.
Alex Black
1
Gibt es eine Möglichkeit zu bestätigen, dass die Bibliothek eines Drittanbieters 64-Bit ist? ZB die .a-Dateien mit einem Tool oder etwas anderem untersuchen?
Alex Black
Wo befindet es sich? Ein Google zeigt, dass es eine Halbkonvention gibt, die es in der / usr / lib64 hat
Daniel A. White
Die lib-Dateien von Drittanbietern sind hier: / home / notroot / workspace / somelib-3 / somelib / target / bin
Alex Black

Antworten:

74

Angenommen, diese Methoden befinden sich in einer der Bibliotheken, sieht es nach einem Bestellproblem aus.

Wenn Bibliotheken mit einer ausführbaren Datei verknüpft werden, erfolgen sie in der Reihenfolge, in der sie deklariert sind.
Außerdem verwendet der Linker nur die Methoden / Funktionen, die zum Auflösen der derzeit ausstehenden Abhängigkeiten erforderlich sind. Wenn eine nachfolgende Bibliothek dann Methoden / Funktionen verwendet, die ursprünglich von den Objekten nicht benötigt wurden, fehlen Abhängigkeiten.

Wie es funktioniert:

  • Nehmen Sie alle Objektdateien und kombinieren Sie sie zu einer ausführbaren Datei
  • Lösen Sie alle Abhängigkeiten zwischen Objektdateien.
  • Für jede Bibliothek in der Reihenfolge:
    • Überprüfen Sie ungelöste Abhängigkeiten und prüfen Sie, ob die Bibliothek sie auflöst.
    • Wenn ja, laden Sie den erforderlichen Teil in die ausführbare Datei.

Beispiel:

Objekte erfordern:

  • Öffnen
  • Schließen
  • BatchRead
  • BatchWrite

Lib 1 bietet:

  • Öffnen
  • Schließen
  • lesen
  • schreiben

Lib 2 bietet

  • BatchRead (verwendet aber lib1: read)
  • BatchWrite (verwendet aber lib1: write)

Wenn so verlinkt:

gcc -o plop plop.o -l1 -l2

Dann kann der Linker die Lese- und Schreibsymbole nicht auflösen.

Aber wenn ich die Anwendung so verlinke:

gcc -o plop plop.o -l2 -l1

Dann wird es richtig verlinken. As l2 löst die Abhängigkeiten BatchRead und BatchWrite auf, fügt aber auch zwei neue hinzu (Lesen und Schreiben). Wenn wir als nächstes mit l1 verknüpfen, werden alle vier Abhängigkeiten aufgelöst.

Martin York
quelle
Beziehen Sie sich auf die Reihenfolge der lib-Dateien in der G ++ - Befehlszeile?
Alex Black
Ja. :-)
Martin York
Ich stelle fest, dass Ihr Beispiel "gcc" verwendet, während meine Frage "g ++" verwendet. Sollte ich stattdessen "gcc" verwenden?
Alex Black
2
Keine Verwendung, was Sie verwendet haben. Wenn Ihr Code C ++ enthält, müssen Sie g ++ verwenden, um die richtigen Standardbibliotheken zu erhalten.
Martin York
3
das hat mir wirklich geholfen! Mein Gruppenprojekt hat plötzlich aufgehört, neue CPP-Dateien aus einer meiner Bibliotheken zu akzeptieren. Es ging nur um undefinierte Referenzen. Dann habe ich die Position gewechselt und es hat als Zauber gewirkt. Im Ernst, es sollte auf einigen Seiten einen spezifischeren Artikel zu diesem Thema geben. Vielen Dank Martin!
Jonathan
166

Dieser Linkerfehler bedeutet normalerweise (meiner Erfahrung nach), dass Sie eine virtuelle Funktion in einer untergeordneten Klasse mit einer Deklaration überschrieben haben, aber keine Definition für die Methode angegeben haben. Zum Beispiel:

class Base
{
    virtual void f() = 0;
}
class Derived : public Base
{
    void f();
}

Aber Sie haben die Definition von f nicht gegeben. Wenn Sie die Klasse verwenden, wird der Linkerfehler angezeigt. Ähnlich wie bei einem normalen Linkerfehler wusste der Compiler, wovon Sie sprachen, aber der Linker konnte die Definition nicht finden. Es ist nur eine sehr schwer zu verstehende Botschaft.

mgiuca
quelle
3
Dankeschön. Ich konnte 2 Stunden lang keine Lösung finden.
Problemoffizier
2
Genau das Problem, das ich hatte. Danke, du hast mir Zeit und Mühe gespart.
Haider
Interessant, dass es in meinem Fall nur mit 'reinen Virtuals' passiert! Die Fehlermeldung ist wirklich irreführend.
Mischmashru
Das war mein Problem. Ich habe vergessen, es auf 0 zu setzen!
Scottc
52

Qt C ++ zeigt diesen Fehler an, wenn Sie eine Klasse so ändern, dass sie jetzt von QObject erbt (dh damit sie jetzt Signale / Slots verwenden kann). Wenn Sie qmake -r ausführen, wird moc aufgerufen und dieses Problem behoben.

Wenn Sie über eine Versionskontrolle mit anderen zusammenarbeiten, möchten Sie einige Änderungen an Ihrer .pro-Datei vornehmen (dh eine leere Zeile hinzufügen / entfernen). Wenn alle anderen Ihre Änderungen erhalten und make ausführen, sieht make, dass sich die .pro-Datei geändert hat, und führt qmake automatisch aus. Dies erspart Ihren Teamkollegen, Ihre Frustration zu wiederholen.

Rick Smith
quelle
Vielen Dank ! Ohne Hinweise sehr schwer zu finden!
Rémy DAVID
1
Wenn Sie vergessen, die Klasse zum HEADER-Abschnitt der * .pro-Datei hinzuzufügen, wird der gleiche Fehler angezeigt. Der Header der Klasse muss HEADER sein, damit moc Q_OBJECT aufnimmt.
Vpicaver
15

Das Problem stellte sich für mich als ziemlich dunkel heraus. Meine Klasse sah so aus:

//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() { }

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
//-----------------------------------------

//-----------------------------------------
// main.h
class derived : public base {
public:
    virtual int foo() ;
};
//-----------------------------------------

//-----------------------------------------
// main.cpp
int main () {
    derived d;
}
//-----------------------------------------

Das Problem liegt im Linker. Meine Header-Datei befand sich irgendwo in einer Bibliothek, aber alle virtuellen Funktionen wurden in der Klassendeklaration als "inline" deklariert. Da (noch) kein Code für die virtuellen Funktionen vorhanden war, hat der Compiler oder Linker es versäumt, tatsächliche Funktionskörper einzurichten. Es konnte auch die vtable nicht erstellt werden.

In meinem Hauptcode, in dem ich von dieser Klasse abgeleitet habe, hat der Linker versucht, meine Klasse mit der Basisklasse und seiner vtable zu verbinden. Aber die vtable war verworfen worden.

Die Lösung bestand darin, mindestens einen der Körper der virtuellen Funktionen außerhalb der Klassendeklaration wie folgt zu deklarieren:

//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() ;   //-- No longer declared 'inline'

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
base::~base() 
{
}
//-----------------------------------------
Phord
quelle
Für mich war es die Unterklasse und ihre virtuelle Funktion
A. Binzxxxxxx
Sie vermissen Semikolon am Ende der Klassendefinition :)
Justme0
9

In Bezug auf Probleme mit Qt4 konnte ich die oben erwähnte Option qmake moc nicht verwenden. Aber das war sowieso nicht das Problem. Ich hatte den folgenden Code in der Klassendefinition:

class ScreenWidget : public QGLWidget
{
   Q_OBJECT        // must include this if you use Qt signals/slots
...
};

Ich musste die Zeile "Q_OBJECT" entfernen, da keine Signale oder Slots definiert waren.

mschachter
quelle
Vielen Dank! Hatte dieses Problem mit g ++ und cmake, mit Code, der in Studio gut kompiliert wurde.
Nicolas Holthaus
8

Ich hatte diese Fehlermeldung. Das Problem war, dass ich einen virtuellen Destruktor in der Header-Datei deklariert habe ,, aber der Körper der virtuellen Funktionen wurde tatsächlich nicht implementiert.

lukeinchina
quelle
5

Dieser Fehler tritt auch auf, wenn wir einfach eine virtuelle Funktion ohne Definition in der Basisklasse deklarieren.

Zum Beispiel:

class Base
{
    virtual void method1(); // throws undefined reference error.

}

Ändern Sie die obige Deklaration in die unten stehende, es wird gut funktionieren.

class Base
{
    virtual void method1()
    {
    }
}
user2376546
quelle
Das hat wirklich bei mir funktioniert! Können Sie erklären, warum das passiert? Ich habe viele Klassen gesehen, in denen eine virtuelle Funktion ohne geschweifte Klammern deklariert ist.
kunal18
@stalin Wenn Sie angeben, ob keine geschweiften Klammern vorhanden sind, bedeutet dies, dass Sie es an einer anderen Stelle implementieren (normalerweise in der CPP-Datei).
Ven
4

In meinem Fall trat das Problem auf, als ich vergaß, die = 0 für eine Funktion in meiner reinen virtuellen Klasse hinzuzufügen. Es wurde behoben, als = 0 hinzugefügt wurde. Das gleiche wie für Frank oben.

class ISettings
{
public: 
    virtual ~ISettings() {};
    virtual void OKFunction() =0;
    virtual void ProblemFunction(); // missing =0   
};

class Settings : ISettings
{
    virtual ~Settings() {};
    void OKFunction();
    void ProblemFunction(); 
};

void Settings::OKFunction()
{
    //stuff
}

void Settings::ProblemFunction()
{
    //stuff
}
Lars Persson
quelle
1

Ich bin jetzt auch über das Thema gestolpert. Die Anwendung definierte eine reine virtuelle Schnittstellenklasse und eine benutzerdefinierte Klasse, die über eine gemeinsam genutzte Bibliothek bereitgestellt wurde, sollte die Schnittstelle implementieren. Beim Verknüpfen der Anwendung beschwerte sich der Linker, dass die gemeinsam genutzte Bibliothek weder vtable und type_info für die Basisklasse bereitstellen würde, noch dass sie irgendwo anders gefunden werden könnten. Es stellte sich heraus, dass ich einfach vergessen habe, eine der Methoden der Schnittstelle rein virtuell zu machen (dh das "= 0" am Ende der Deklaration weggelassen. Sehr rudimentär, immer noch leicht zu übersehen und rätselhaft, wenn Sie die Linkerdiagnose nicht mit der verbinden können Ursache.

frank
quelle
0

Ich hatte diese Fehlermeldung, als ich "Hallo Welt" wie Dinge mit Qt versuchte. Die Probleme wurden behoben, indem der qt moc (Meta Object Compiler) korrekt ausgeführt und + einschließlich dieser von moc generierten Dateien korrekt kompiliert wurde.

awallin
quelle
0

Wenn Sie eine Basisklasse mit einer reinen virtuellen Funktion haben, stellen Sie sicher, dass Ihr Basisklassenkonstruktor und -destruktor einen Body hat, andernfalls schlägt der Linker fehl.

sumeet
quelle
Es hat eigentlich nichts mit dem Konstruktor oder Destruktor zu tun. Wenn Sie diese nicht haben, schlägt ein allgemeiner Linker fehl. Es muss eine virtuelle Methode fehlen, um einen Linkerfehler in der vtable zu erhalten.
Mysticial
0

Ich stelle dies für zukünftige Besucher:

Wenn beim Erstellen eines ExceptionObjekts der Fehler angezeigt wird, liegt die Ursache wahrscheinlich in der fehlenden Definition der what()virtuellen Funktion.

Mostafa Talebi
quelle