Ich bin gerade auf den folgenden Fehler gestoßen (und habe die Lösung online gefunden, aber sie ist im Stapelüberlauf nicht vorhanden):
(.gnu.linkonce. [stuff]): undefinierter Verweis auf [method] [Objektdatei] :(. gnu.linkonce. [stuff]): undefinierter Verweis auf typeinfo für [Klassenname]
Warum könnte man einen dieser "undefinierten Verweise auf typeinfo" Linkerfehler bekommen?
(Bonuspunkte, wenn Sie erklären können, was sich hinter den Kulissen abspielt.)
virtual void abc() =0;
(wenn die Basisversion nie aufgerufen wird)abc()
, können Sie leicht vergessen,abc()
die abgeleitete Klasse neu zu definieren und denken, dass alles in Ordnung ist, da Sie die Funktion weiterhin problemlos aufrufen können. Eine gute Vorgehensweise zum Implementieren von reinen virtuellen Funktionen finden Sie in diesem Artikel . Hiermit wird die Funktion "Reine virtuelle Funktion aufgerufen" gedruckt und anschließend das Programm zum Absturz gebracht.= 0;
.Antworten:
Ein möglicher Grund ist, dass Sie eine virtuelle Funktion deklarieren, ohne sie zu definieren.
Wenn Sie es deklarieren, ohne es in derselben Kompilierungseinheit zu definieren, geben Sie an, dass es an einer anderen Stelle definiert ist. Dies bedeutet, dass die Linkerphase versucht, es in einer der anderen Kompilierungseinheiten (oder Bibliotheken) zu finden.
Ein Beispiel für die Definition der virtuellen Funktion ist:
In diesem Fall fügen Sie der Deklaration eine Definition hinzu, was bedeutet, dass der Linker sie später nicht auflösen muss.
Die Linie
deklariert
fn()
ohne es zu definieren und verursacht die Fehlermeldung, nach der Sie gefragt haben.Es ist dem Code sehr ähnlich:
Dies besagt, dass die Ganzzahl
i
in einer anderen Kompilierungseinheit deklariert ist, die zur Verbindungszeit aufgelöst werden muss (andernfallspi
kann sie nicht auf ihre Adresse gesetzt werden).quelle
virtual void fn() = 0
eine Definition ist. Es ist keine Definition, sondern eine bloße Erklärung . Der einzige Grund, warum der Linker nicht versucht, das Problem zu beheben, besteht darin, dass der entsprechende VMT-Eintrag nicht auf einen Funktionskörper verweist (höchstwahrscheinlich einen Nullzeiger enthält). Niemand verbietet Ihnen jedoch, diese reine virtuelle Funktion nicht virtuell aufzurufen, dh unter Verwendung eines vollständig qualifizierten Namens. In diesem Fall wird der Linker wird für den Körper aussieht, und Sie werden die Funktion definieren. Und ja, Sie können einen Körper für eine reine virtuelle Funktion definieren.Dies kann auch passieren, wenn Sie mischen
-fno-rtti
und-frtti
codieren. Dann müssen Sie sicherstellen, dass für jede Klasse, auf dietype_info
im-frtti
Code zugegriffen wird , ihre Schlüsselmethode kompiliert wird-frtti
. Ein solcher Zugriff kann erfolgen, wenn Sie ein Objekt der Klasse erstellen, verwendendynamic_cast
usw.[ Quelle ]
quelle
Dies tritt auf, wenn deklarierten (nicht reinen) virtuellen Funktionen Körper fehlen. In Ihrer Klassendefinition so etwas wie:
Sollte definiert werden (Inline oder in einer verknüpften Quelldatei):
Oder als rein virtuell deklariert:
quelle
Zitat aus dem gcc-Handbuch :
Und etwas früher auf derselben Seite:
Dieser Fehler tritt also auf, wenn der "Schlüsselmethode" die Definition fehlt, wie andere bereits erwähnte Antworten zeigen.
quelle
Wenn Sie eine .so mit einer anderen verknüpfen, besteht eine weitere Möglichkeit darin, mit "-fvisibility = hidden" in gcc oder g ++ zu kompilieren. Wenn beide .so-Dateien mit "-fvisibility = hidden" erstellt wurden und sich die Schlüsselmethode nicht in derselben .so-Datei wie eine andere Implementierung der virtuellen Funktion befindet, wird in letzterer die vtable oder typeinfo der ersteren nicht angezeigt. Für den Linker sieht dies wie eine nicht implementierte virtuelle Funktion aus (wie in den Antworten von paxdiablo und cdleary).
In diesem Fall müssen Sie eine Ausnahme für die Sichtbarkeit der Basisklasse mit machen
in der Klassendeklaration. Zum Beispiel,
Eine andere Lösung besteht natürlich darin, "-fvisibility = hidden" nicht zu verwenden. Dies erschwert den Compiler und den Linker, was sich möglicherweise nachteilig auf die Codeleistung auswirkt.
quelle
Die vorherigen Antworten sind korrekt, aber dieser Fehler kann auch durch den Versuch verursacht werden, typeid für ein Objekt einer Klasse zu verwenden, das keine virtuellen Funktionen hat. Für C ++ RTTI ist eine vtable erforderlich, sodass für Klassen, für die Sie eine Typidentifizierung durchführen möchten, mindestens eine virtuelle Funktion erforderlich ist.
Wenn Sie möchten, dass Typinformationen für eine Klasse funktionieren, für die Sie keine virtuellen Funktionen benötigen, machen Sie den Destruktor virtuell.
quelle
Ich habe nur ein paar Stunden mit diesem Fehler verbracht, und während die anderen Antworten hier mir geholfen haben zu verstehen, was los war, haben sie mein spezielles Problem nicht behoben.
Ich arbeite an einem Projekt, das mit
clang++
und kompiliert wirdg++
. Ich hatte keine Verbindungsprobleme mitclang++
, bekam aber denundefined reference to 'typeinfo for
Fehler mitg++
.Der wichtige Punkt: Reihenfolge verknüpfen mit
g++
. Wenn Sie die Bibliotheken, die Sie verknüpfen möchten, in einer falschen Reihenfolge auflisten, wird dertypeinfo
Fehler angezeigt.In dieser SO-Frage finden Sie weitere Informationen zum Verknüpfen der Bestellung mit
gcc
/g++
.quelle
Mögliche Lösungen für Code, der sich mit RTTI- und Nicht-RTTI-Bibliotheken befasst:
a) Kompilieren Sie alles mit -frtti oder -fno-rtti neu.
b) Wenn a) für Sie nicht möglich ist, versuchen Sie Folgendes:
Angenommen, libfoo wird ohne RTTI erstellt. Ihr Code verwendet libfoo und wird mit RTTI kompiliert. Wenn Sie in libfoo eine Klasse (Foo) mit Virtuals verwenden, tritt wahrscheinlich ein Link-Time-Fehler auf, der besagt: Typeinfo für Klasse Foo fehlt.
Definieren Sie eine andere Klasse (z. B. FooAdapter), die keine virtuelle Klasse hat und von Ihnen verwendete Anrufe an Foo weiterleitet.
Kompilieren Sie FooAdapter in einer kleinen statischen Bibliothek, die kein RTTI verwendet und nur von libfoo-Symbolen abhängt. Geben Sie einen Header dafür an und verwenden Sie diesen stattdessen in Ihrem Code (der RTTI verwendet). Da FooAdapter keine virtuelle Funktion hat, hat es keine Typeinfo und Sie können Ihre Binärdatei verknüpfen. Wenn Sie viele verschiedene Klassen von libfoo verwenden, ist diese Lösung möglicherweise nicht praktisch, aber es ist ein Anfang.
quelle
Ähnlich wie bei der obigen Diskussion zu RTTI, NO-RTTI kann dieses Problem auch auftreten, wenn Sie dynamic_cast verwenden und den Objektcode, der die Klassenimplementierung enthält, nicht einschließen.
Ich bin auf dieses Problem gestoßen, als ich auf Cygwin aufgebaut und dann Code nach Linux portiert habe. Die make-Dateien, die Verzeichnisstruktur und sogar die gcc-Versionen (4.8.2) waren in beiden Fällen identisch, aber der Code wurde unter Cygwin verknüpft und funktionierte korrekt, konnte jedoch unter Linux nicht verknüpft werden. Red Hat Cygwin hat anscheinend Compiler / Linker-Änderungen vorgenommen, die die Anforderung der Objektcode-Verknüpfung vermeiden.
Die Linux-Linker-Fehlermeldung hat mich ordnungsgemäß zur Zeile dynamic_cast weitergeleitet, aber in früheren Nachrichten in diesem Forum habe ich eher nach fehlenden Funktionsimplementierungen als nach dem eigentlichen Problem gesucht: fehlender Objektcode. Meine Problemumgehung bestand darin, eine virtuelle Typfunktion in der Basisklasse und der abgeleiteten Klasse zu ersetzen, z. B. virtual int isSpecialType (), anstatt dynamic_cast zu verwenden. Diese Technik vermeidet die Notwendigkeit, Objektimplementierungscode zu verknüpfen, damit dynamic_cast ordnungsgemäß funktioniert.
quelle
In der Basisklasse (einer abstrakten Basisklasse) deklarieren Sie einen virtuellen Destruktor. Da Sie einen Destruktor nicht als reine virtuelle Funktion deklarieren können, müssen Sie ihn auch hier in der abstrakten Klasse definieren, nur eine Dummy-Definition wie virtual ~ base ( ) {} reicht aus oder in einer der abgeleiteten Klassen.
Wenn Sie dies nicht tun, werden Sie zum Zeitpunkt der Verknüpfung in einem "undefinierten Symbol" angezeigt. Da VMT einen Eintrag für alle rein virtuellen Funktionen mit einem übereinstimmenden NULL hat, wird die Tabelle abhängig von der Implementierung in der abgeleiteten Klasse aktualisiert. Für die nicht reinen, aber virtuellen Funktionen wird die Definition zum Zeitpunkt der Verknüpfung benötigt, damit die VMT-Tabelle aktualisiert werden kann.
Verwenden Sie c ++ filt, um das Symbol zu entwirren. Wie $ c ++ filt gibt _ZTIN10storageapi8BaseHostE so etwas wie "typeinfo for storageapi :: BaseHost" aus.
quelle
Ich habe gerade viele dieser Fehler bekommen. Was passiert ist, dass ich eine Nur-Header-Datei-Klasse in eine Header-Datei und eine CPP-Datei aufgeteilt habe. Ich habe mein Build-System jedoch nicht aktualisiert, sodass die CPP-Datei nicht kompiliert wurde. Unter einfach undefinierten Verweisen auf die Funktionen, die im Header deklariert, aber nicht implementiert wurden, habe ich viele dieser typeinfo-Fehler erhalten.
Die Lösung bestand darin, das Build-System erneut auszuführen, um die neue CPP-Datei zu kompilieren und zu verknüpfen.
quelle
In meinem Fall habe ich eine Drittanbieter-Bibliothek mit Header-Dateien und so weiter verwendet. Ich habe eine Klasse unterklassifiziert, und ein Linkfehler wie dieser ist aufgetreten, als ich versucht habe, meine Unterklasse zu instanziieren.
Wie von @sergiy erwähnt, da ich wusste, dass es das Problem von 'rtti' sein könnte, gelang es mir, es zu umgehen, indem ich die Konstruktorimplementierung in eine separate CPP-Datei legte und '-fno-rtti'-Kompilierungsflags auf die Datei anwendete . Es funktioniert gut.
Da mir der interne Fehler dieses Links immer noch nicht ganz klar ist, bin ich mir nicht sicher, ob meine Lösung allgemein ist. Ich denke jedoch, dass es einen Versuch wert ist, bevor Sie den Adapter wie von @francois erwähnt ausprobieren. und wenn alle Quellcodes verfügbar sind (in meinem Fall nicht), kompilieren Sie sie nach Möglichkeit besser mit '-frtti'.
Wenn Sie meine Lösung ausprobieren möchten, versuchen Sie, die separate Datei so einfach wie möglich zu gestalten, und verwenden Sie keine ausgefallenen Funktionen von C ++. Achten Sie besonders auf Boost-bezogene Dinge, da ein Großteil davon von RTI abhängt.
quelle
Ich habe den gleichen Fehler erhalten, als meine Schnittstelle (mit allen reinen virtuellen Funktionen) eine weitere Funktion benötigte und ich vergaß, sie auf "null" zu setzen.
ich hatte
class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };
Last vaClose ist nicht virtuell, daher wusste die Kompilierung nicht, wo sie implementiert werden sollte, und war dadurch verwirrt. Meine Nachricht war:
Einfacher Wechsel von
zu
Das Problem wurde behoben. ich hoffe es hilft
quelle
Ich stoße auf eine Situation, die selten ist, aber dies kann anderen Freunden in einer ähnlichen Situation helfen. Ich muss auf einem älteren System mit gcc 4.4.7 arbeiten. Ich muss Code mit Unterstützung für C ++ 11 oder höher kompilieren, damit ich die neueste Version von gcc 5.3.0 erstelle. Beim Erstellen meines Codes und beim Verknüpfen mit den Abhängigkeiten, wenn die Abhängigkeit mit einem älteren Compiler erstellt wurde, wurde der Fehler "undefinierter Verweis auf" angezeigt, obwohl ich den Verknüpfungspfad mit -L / path / to / lib -llibname klar definiert habe. Einige Pakete wie Boost und Projekte, die mit cmake erstellt wurden, tendieren normalerweise dazu, den älteren Compiler zu verwenden, und sie verursachen normalerweise solche Probleme. Sie müssen einen langen Weg gehen, um sicherzustellen, dass sie den neueren Compiler verwenden.
quelle
In meinem Fall handelt es sich lediglich um ein Problem mit der Bibliotheksabhängigkeit, selbst wenn ich einen Aufruf von dynamic_cast habe. Nachdem dem Makefile genügend Abhängigkeiten hinzugefügt wurden, war dieses Problem behoben.
quelle
Überprüfen Sie, ob Ihre Abhängigkeiten ohne kompiliert wurden
-f-nortti
.Für einige Projekte müssen Sie es explizit festlegen, wie in RocksDB:
quelle
In meinem Fall war es eine virtuelle Funktion in einer Schnittstellenklasse, die nicht als rein virtuell definiert wurde.
Ich habe das
= 0
bisschen vergessen .quelle