Frage Was ist eine nicht definierte Referenz / ein nicht aufgelöster externer Symbolfehler und wie kann ich sie beheben?


Was sind nicht definierte Referenzen / ungelöste externe Symbolfehler? Was sind häufige Ursachen und wie kann man sie beheben / verhindern?

Fühlen Sie sich frei, Ihre eigenen zu bearbeiten / hinzuzufügen.


1194
2017-09-24 22:27


Ursprung


Antworten:


Das Kompilieren eines C ++ - Programms erfolgt in mehreren Schritten, wie von 2.2  (Kredite an Keith Thompson für die Referenz):

Der Vorrang unter den Syntaxregeln der Übersetzung wird durch die folgenden Phasen festgelegt [siehe Fußnote].

  1. Physikalische Quelldateizeichen werden in einer implementierungsdefinierten Weise dem grundlegenden Quellzeichensatz zugeordnet   (Einführung neuer Zeilenzeichen für Zeilenende-Indikatoren) if   notwendig. [SNIP]
  2. Jede Instanz eines Backslash-Zeichens (\), unmittelbar gefolgt von einem Zeichen für eine neue Zeile, wird gelöscht, und es werden physische Quellzeilen verbunden   Bilden Sie logische Quellzeilen. [SNIP]
  3. Die Quelldatei wird in Vorverarbeitungstoken (2.5) und Sequenzen von Leerzeichen (einschließlich Kommentaren) zerlegt. [SNIP]
  4. Vorverarbeitungsdirektiven werden ausgeführt, Makroaufrufe werden erweitert, und _pragma-Ausdrücke für unäre Operatoren werden ausgeführt. [SNIP]
  5. Jedes Quellenzeichensatzelement in einem Zeichenliteral oder einem Zeichenfolgenliteral sowie jede Escape-Sequenz und Universal-Charaktername   in einem Zeichenliteral oder einem nicht-rohen Zeichenfolge-Literal wird in konvertiert   das entsprechende Mitglied des Ausführungszeichensatzes; [SNIP]
  6. Benachbarte Zeichenfolge-Literaltoken werden verkettet.
  7. Leerraumzeichen, die Token trennen, sind nicht länger von Bedeutung. Jedes Vorverarbeitungstoken wird in ein Token umgewandelt. (2.7). Das   resultierende Token werden syntaktisch und semantisch analysiert und analysiert   übersetzt als Übersetzungseinheit. [SNIP]
  8. Übersetzte Übersetzungseinheiten und Instanziierungseinheiten werden wie folgt kombiniert: [SNIP]
  9. Alle externen Entity-Referenzen sind aufgelöst. Bibliothekskomponenten werden verknüpft, um externe Referenzen auf Entitäten zu erfüllen, die nicht in der Datenbank definiert sind   aktuelle Übersetzung Alle diese Übersetzerausgaben werden in a gesammelt   Programmabbild, das zur Ausführung benötigte Informationen in seinem enthält   Ausführungsumgebung. (Hervorhebung von mir)

[Fußnote] Implementierungen müssen sich so verhalten, als würden diese separaten Phasen auftreten, obwohl in der Praxis unterschiedliche Phasen zusammengefaltet werden könnten.

Die angegebenen Fehler treten während dieser letzten Kompilierungsstufe auf, die üblicherweise als Verknüpfung bezeichnet wird. Es bedeutet im Grunde, dass Sie eine Reihe von Implementierungsdateien in Objektdateien oder Bibliotheken kompiliert haben und nun möchten, dass sie zusammen funktionieren.

Angenommen, Sie haben ein Symbol definiert a im a.cpp. Jetzt, b.cpp  erklärt dieses Symbol und benutzte es. Vor dem Verknüpfen wird einfach vorausgesetzt, dass dieses Symbol definiert wurde irgendwo, aber es kümmert sich noch nicht wo. Die Verknüpfungsphase ist dafür verantwortlich, das Symbol zu finden und korrekt zu verknüpfen b.cpp (Nun, eigentlich zu dem Objekt oder der Bibliothek, die es benutzt).

Wenn Sie Microsoft Visual Studio verwenden, sehen Sie, dass Projekte generiert werden .lib Dateien. Diese enthalten eine Tabelle exportierter Symbole und eine Tabelle importierter Symbole. Die importierten Symbole werden für die Bibliotheken aufgelöst, mit denen Sie verknüpfen, und die exportierten Symbole werden für die Bibliotheken bereitgestellt, die diese verwenden .lib (wenn überhaupt).

Ähnliche Mechanismen existieren für andere Compiler / Plattformen.

Häufige Fehlermeldungen sind error LNK2001, error LNK1120, error LNK2019 zum Microsoft Visual Studio und undefined reference to  SymbolName zum GCC.

Der Code:

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

erzeugt folgende Fehler mit GCC:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

und ähnliche Fehler mit Microsoft Visual Studio:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

Häufige Ursachen sind:


693
2017-09-24 22:27



Klassenmitglieder:

Ein reines virtual Destruktor benötigt eine Implementierung.

Um einen Destruktor rein zu deklarieren, müssen Sie ihn noch definieren (im Gegensatz zu einer normalen Funktion):

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

Dies geschieht, weil Basisklassendestruktoren aufgerufen werden, wenn das Objekt implizit zerstört wird. Daher ist eine Definition erforderlich.

virtual Methoden müssen entweder implementiert oder als rein definiert werden.

Das ist ähnlich wie beivirtual Methoden ohne Definition, mit dem zusätzlichen Argument, dass Die reine Deklaration erzeugt eine Dummy-V-Tabelle, und Sie erhalten möglicherweise den Linker-Fehler, ohne die Funktion zu verwenden:

struct X
{
    virtual void foo();
};
struct Y : X
{
   void foo() {}
};
int main()
{
   Y y; //linker error although there was no call to X::foo
}

Damit dies funktioniert, deklarieren Sie X::foo() so rein:

struct X
{
    virtual void foo() = 0;
};

Nicht-virtual Klassenmitglieder

Einige Mitglieder müssen definiert werden, auch wenn sie nicht explizit verwendet werden:

struct A
{ 
    ~A();
};

Folgendes würde den Fehler ergeben:

A a;      //destructor undefined

Die Implementierung kann inline sein, in der Klassendefinition selbst:

struct A
{ 
    ~A() {}
};

oder draußen:

A::~A() {}

Wenn die Implementierung außerhalb der Klassendefinition, aber in einem Header ist, müssen die Methoden als markiert werden inline um eine Mehrfachdefinition zu verhindern.

Alle verwendeten Member-Methoden müssen definiert werden, wenn sie verwendet werden.

Ein häufiger Fehler ist es, zu vergessen, den Namen zu qualifizieren:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

Die Definition sollte sein

void A::foo() {}

static Datenelemente müssen außerhalb der Klasse in a definiert werden einzelne Übersetzungseinheit:

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

Ein Initialisierer kann für ein bereitgestellt werden static  const Datenelement des Integral- oder Enumerationstyps innerhalb der Klassendefinition; Für die odr-Verwendung dieses Members ist jedoch weiterhin eine Namensbereichsdefinition erforderlich, wie oben beschrieben. C ++ 11 ermöglicht die Initialisierung innerhalb der Klasse für alle static constDatenmitglieder.


148
2017-09-24 23:38



Fehler beim Verknüpfen mit geeigneten Bibliotheken / Objektdateien oder Kompilieren von Implementierungsdateien

Üblicherweise erzeugt jede Übersetzungseinheit eine Objektdatei, die die Definitionen der in dieser Übersetzungseinheit definierten Symbole enthält. Um diese Symbole zu verwenden, müssen Sie mit diesen Objektdateien verknüpfen.

Unter gcc Sie geben alle Objektdateien an, die in der Befehlszeile miteinander verknüpft werden sollen, oder kompilieren die Implementierungsdateien zusammen.

g++ -o test objectFile1.o objectFile2.o -lLibraryName

Das libraryName Hier ist nur der nackte Name der Bibliothek, ohne plattformspezifische Zusätze. So z.B. Unter Linux werden üblicherweise Bibliotheksdateien aufgerufen libfoo.so aber du würdest nur schreiben -lfoo. Unter Windows wird dieselbe Datei möglicherweise aufgerufen foo.lib, aber Sie würden das gleiche Argument verwenden. Möglicherweise müssen Sie das Verzeichnis hinzufügen, in dem diese Dateien gefunden werden können -L‹directory›. Achte darauf, dass du kein Leerzeichen hinterher schreibst -l oder -L.

Zum XCode: Fügen Sie die Suchpfade für die Benutzerkopfzeile hinzu -> fügen Sie den Bibliothekssuchpfad hinzu -> ziehen Sie die tatsächliche Bibliotheksreferenz in den Projektordner und legen Sie sie dort ab.

Unter MSVSBei Dateien, die zu einem Projekt hinzugefügt werden, werden die Objektdateien automatisch miteinander verknüpft und a lib Datei würde erzeugt werden (im allgemeinen Gebrauch). Um die Symbole in einem separaten Projekt zu verwenden, würdest du müssen die einschließen lib Dateien in den Projekteinstellungen. Dies geschieht im Linker-Abschnitt der Projekteigenschaften, in Input -> Additional Dependencies. (der Weg zum lib Datei sollte sein hinzugefügt in Linker -> General -> Additional Library Directories) Wenn Sie eine Bibliothek eines Drittanbieters verwenden, die mit einem lib Datei, führt dies normalerweise zu dem Fehler.

Es kann auch vorkommen, dass Sie vergessen, die Datei zur Kompilierung hinzuzufügen. In diesem Fall wird die Objektdatei nicht generiert. Im gcc Sie würden die Dateien zur Befehlszeile hinzufügen. Im MSVS Wenn Sie die Datei zum Projekt hinzufügen, wird sie automatisch kompiliert (obwohl Dateien manuell einzeln vom Build ausgeschlossen werden können).

Bei der Windows-Programmierung besteht das verräterische Zeichen, dass Sie keine erforderliche Bibliothek verknüpft haben, darin, dass der Name des nicht aufgelösten Symbols mit beginnt __imp_. Suchen Sie den Namen der Funktion in der Dokumentation, und es sollte angeben, welche Bibliothek Sie verwenden müssen. Beispielsweise legt MSDN die Information in einem Feld an dem Ende jeder Funktion in einem Abschnitt, der als "Bibliothek" bezeichnet wird, an.


97
2017-09-24 23:37



Deklariert, hat aber keine Variable oder Funktion definiert.

Eine typische Variablendeklaration ist

extern int x;

Da dies nur eine Erklärung ist, a einzelne Definition wird gebraucht. Eine entsprechende Definition wäre:

int x;

Das folgende Beispiel würde einen Fehler erzeugen:

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

Ähnliche Bemerkungen gelten für Funktionen. Eine Funktion zu deklarieren, ohne sie zu definieren, führt zu dem Fehler:

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

Achten Sie darauf, dass die von Ihnen implementierte Funktion genau der von Ihnen angegebenen Funktion entspricht. Zum Beispiel haben Sie möglicherweise nicht übereinstimmende CV-Qualifikationsmerkmale:

void foo(int& x);
int main()
{
   int x;
   foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)

Weitere Beispiele für Fehlanpassungen sind

  • Funktion / Variable in einem Namespace deklariert, in einem anderen definiert.
  • Funktion / Variable, die als Klassenmember deklariert ist, definiert als global (oder umgekehrt).
  • Funktionsrückgabetyp, Parameternummer und -typen sowie Aufrufkonvention stimmen nicht alle genau überein.

Die Fehlermeldung vom Compiler gibt Ihnen oft die vollständige Deklaration der Variable oder Funktion, die deklariert, aber nie definiert wurde. Vergleichen Sie es genau mit der Definition, die Sie angegeben haben. Stellen Sie sicher, dass jedes Detail übereinstimmt.


91
2017-09-24 23:38



Die Reihenfolge, in der abhängige verknüpfte Bibliotheken angegeben sind, ist falsch.

Die Reihenfolge, in der Bibliotheken verknüpft sind, spielt eine Rolle, wenn die Bibliotheken voneinander abhängig sind. Im Allgemeinen, wenn Bibliothek A hängt von der Bibliothek ab B, dann libA  MUSS erscheint vorher libB in den Linker-Flags.

Beispielsweise:

// B.h
#ifndef B_H
#define B_H

struct B {
    B(int);
    int x;
};

#endif

// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}

// A.h
#include "B.h"

struct A {
    A(int x);
    B b;
};

// A.cpp
#include "A.h"

A::A(int x) : b(x) {}

// main.cpp
#include "A.h"

int main() {
    A a(5);
    return 0;
};

Erstellen Sie die Bibliotheken:

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

Kompilieren:

$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

Also nochmal wiederholen, die Reihenfolge Macht Angelegenheit!


73
2017-07-10 11:46



Was ist eine "undefinierte Referenz / ungelöstes äußeres Symbol"?

Ich werde versuchen zu erklären, was eine "undefinierte Referenz / ein ungelöstes externes Symbol" ist.

hinweis: ich benutze g ++ und Linux und alle beispiele dafür

Zum Beispiel haben wir einen Code

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

und

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

Erstellen Sie Objektdateien

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

Nach der Assemblerphase haben wir eine Objektdatei, die alle zu exportierenden Symbole enthält. Schau dir die Symbole an

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

Ich habe einige Zeilen aus der Ausgabe abgelehnt, weil sie nicht wichtig sind

Wir sehen also folgende Symbole zum Exportieren.

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp exportiert nichts und wir haben keine seiner Symbole gesehen

Verknüpfen Sie unsere Objektdateien

$ g++ src1.o src2.o -o prog

und führe es aus

$ ./prog
123

Linker sieht exportierte Symbole und verknüpft sie. Jetzt versuchen wir Zeilen in src2.cpp wie hier auskommentieren

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

und erstellen Sie eine Objektdatei neu

$ g++ -c src2.cpp -o src2.o

OK (keine Fehler), weil wir nur eine Objektdatei erstellen, die Verknüpfung ist noch nicht abgeschlossen. Versuchen Sie zu verlinken

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

Es ist passiert, weil unser local_var_name statisch ist, d. H. Er ist für andere Module nicht sichtbar. Jetzt tiefer. Holen Sie sich die Übersetzungsphase

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

Also, wir haben gesehen, dass es keine Bezeichnung für local_var_name gibt, deshalb hat linker es nicht gefunden. Aber wir sind Hacker :) und wir können es beheben. Öffne src1.s in deinem Texteditor und ändere es

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

zu

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

Sie sollten wie folgt haben

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

Wir haben die Sichtbarkeit von local_var_name geändert und den Wert auf 456789 festgelegt. Versuchen Sie, eine Objektdatei daraus zu erstellen

$ g++ -c src1.s -o src2.o

ok, siehe Realdelf-Ausgabe (Symbole)

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

Jetzt hat local_var_name Bind GLOBAL (war LOKAL)

Verknüpfung

$ g++ src1.o src2.o -o prog

und führe es aus

$ ./prog 
123456789

ok, wir hacken es :)

Als Ergebnis - ein "undefinierter Verweis / nicht aufgelöster externer Symbolfehler" tritt auf, wenn der Linker globale Symbole in den Objektdateien nicht finden kann.


63
2017-09-24 23:39



Symbole wurden in einem C-Programm definiert und in C ++ - Code verwendet.

Die Funktion (oder Variable) void foo() wurde in einem C-Programm definiert und Sie versuchen, es in einem C ++ - Programm zu verwenden:

void foo();
int main()
{
    foo();
}

Der C ++ - Linker erwartet, dass die Namen verfälscht werden, also müssen Sie die Funktion wie folgt deklarieren:

extern "C" void foo();
int main()
{
    foo();
}

Äquivalent, anstatt in einem C-Programm definiert zu sein, die Funktion (oder Variable) void foo() wurde in C ++ aber mit C-Verknüpfung definiert:

extern "C" void foo();

und Sie versuchen, es in einem C ++ - Programm mit C ++ - Verknüpfung zu verwenden.

Wenn eine gesamte Bibliothek in einer Header-Datei enthalten ist (und als C-Code kompiliert wurde); das Include muss wie folgt sein;

extern "C" {
    #include "cheader.h"
}

60
2017-12-03 18:11



Wenn alles andere fehlschlägt, kompilieren Sie erneut.

Ich war kürzlich in der Lage, einen unaufgelösten externen Fehler in Visual Studio 2012 loszuwerden, indem ich nur die beleidigende Datei neu kompiliere. Als ich neu baute, ging der Fehler weg.

Dies geschieht normalerweise, wenn zwei (oder mehr) Bibliotheken eine zyklische Abhängigkeit haben. Bibliothek A versucht, Symbole in B.lib zu verwenden, und Bibliothek B versucht, Symbole aus A.lib zu verwenden. Es gibt keine, mit denen man anfangen könnte. Wenn Sie versuchen, A zu kompilieren, schlägt der Verbindungsschritt fehl, da B.lib nicht gefunden werden kann. A.lib wird generiert, aber keine dll. Dann kompilieren Sie B, was erfolgreich ist und B.lib generiert. Das erneute Kompilieren von A funktioniert jetzt, weil B.lib jetzt gefunden wird.


58
2017-09-24 23:39



Fehler beim Importieren / Exportieren von Methoden / Klassen über Module / DLL (Compiler-spezifisch).

In MSVS müssen Sie angeben, welche Symbole exportiert und importiert werden sollen __declspec(dllexport) und __declspec(dllimport).

Diese doppelte Funktionalität wird normalerweise durch die Verwendung eines Makros erzielt:

#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif

Das Makro THIS_MODULE würde nur in dem Modul definiert, das die Funktion exportiert. Auf diese Weise, die Erklärung:

DLLIMPEXP void foo();

expandiert nach

__declspec(dllexport) void foo();

und weist den Compiler an, die Funktion zu exportieren, da das aktuelle Modul seine Definition enthält. Wenn Sie die Deklaration in ein anderes Modul aufnehmen, würde es sich auf erweitern

__declspec(dllimport) void foo();

und teilt dem Compiler mit, dass die Definition in einer der Bibliotheken ist, mit denen Sie verlinkt haben (siehe auch) 1)).

Sie können ähnliche Klassen importieren / exportieren:

class DLLIMPEXP X
{
};

50
2017-09-24 23:38



Template-Implementierungen nicht sichtbar

Nichtspezialisierte Vorlagen müssen ihre Definitionen für alle Übersetzungseinheiten sichtbar machen, die sie verwenden. Das bedeutet, dass Sie die Definition einer Vorlage nicht trennen können zu einer Implementierungsdatei. Wenn Sie die Implementierung trennen müssen, ist die übliche Problemumgehung ein impl Datei, die Sie am Ende der Kopfzeile hinzufügen, die Deklariert die Vorlage. Eine häufige Situation ist:

template<class T>
struct X
{
    void foo();
};

int main()
{
    X<int> x;
    x.foo();
}

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}

Um dies zu beheben, müssen Sie die Definition von verschieben X::foo an die Header-Datei oder an einen Ort, der für die Übersetzungseinheit sichtbar ist, die sie verwendet.

Spezialisierte Vorlagen können in einer Implementierungsdatei implementiert werden und die Implementierung muss nicht sichtbar sein, aber die Spezialisierung muss vorher deklariert werden.

Für weitere Erklärungen und eine andere mögliche Lösung (explizite Instantiierung) siehe diese Frage und Antwort.


48
2018-04-13 16:42



Dies ist eine der verwirrendsten Fehlermeldungen, die jeder VC ++ - Programmierer immer wieder gesehen hat. Lassen Sie uns zunächst Klarheit schaffen.

A. Was ist ein Symbol? Kurz gesagt, ein Symbol ist ein Name. Es kann sich um einen Variablennamen, einen Funktionsnamen, einen Klassennamen, einen Typedef-Namen oder irgendetwas anderes handeln als die Namen und Zeichen, die zur C ++ - Sprache gehören. Es ist benutzerdefiniert oder wird durch eine Abhängigkeitsbibliothek eingeführt (ein anderer Benutzer definiert).

B. Was ist extern? In VC ++ wird jede Quelldatei (.cpp, .c usw.) als eine Übersetzungseinheit betrachtet, der Compiler kompiliert jeweils eine Einheit und erzeugt eine Objektdatei (.obj) für die aktuelle Übersetzungseinheit. (Beachten Sie, dass jede Header-Datei, die diese Quelldatei enthält, vorverarbeitet wird und als Teil dieser Übersetzungseinheit betrachtet wird) Alles innerhalb einer Übersetzungseinheit wird als intern betrachtet, alles andere wird als extern betrachtet. In C ++ können Sie auf ein externes Symbol verweisen, indem Sie Schlüsselwörter wie verwenden extern, __declspec (dllimport) und so weiter.

C. Was ist "Entschlossenheit"? Resolve ist ein Linking-Term. In Linking-Zeit versucht Linker, die externe Definition für jedes Symbol in Objektdateien zu finden, die ihre Definition intern nicht finden können. Der Umfang dieses Suchprozesses beinhaltet:

  • Alle Objektdateien, die beim Kompilieren generiert wurden
  • Alle Bibliotheken (.lib), die explizit oder implizit sind als zusätzliche Abhängigkeiten dieser Bauanwendung angegeben.

Dieser Suchvorgang wird als Auflösen bezeichnet.

D. Schließlich, warum ungelöstes äußeres Symbol? Wenn der Linker die externe Definition für ein Symbol, das intern keine Definition hat, nicht finden kann, meldet er einen nicht aufgelösten externen Symbolfehler.

E. Mögliche Ursachen für LNK2019: Ungelöster externer Symbolfehler. Wir wissen bereits, dass dieser Fehler darauf zurückzuführen ist, dass der Linker die Definition externer Symbole nicht finden konnte. Die möglichen Ursachen können wie folgt sortiert werden:

  1. Definition existiert

Zum Beispiel, wenn wir eine Funktion namens foo haben, die in a.cpp definiert ist:

int foo()
{
    return 0;
}

In b.cpp wollen wir die Funktion foo aufrufen, also fügen wir hinzu

void foo();

um die Funktion foo () zu deklarieren und sie in einem anderen Funktionskörper aufzurufen, sagen wir bar():

void bar()
{
    foo();
}

Wenn Sie nun diesen Code erstellen, erhalten Sie einen LNK2019-Fehler, der darauf hinweist, dass foo ein ungelöstes Symbol ist. In diesem Fall wissen wir, dass foo () seine Definition in a.cpp hat, aber anders als die, die wir aufrufen (anderer Rückgabewert). Dies ist der Fall, dass eine Definition existiert.

  1. Definition existiert nicht

Wenn wir einige Funktionen in einer Bibliothek aufrufen möchten, wird die Importbibliothek jedoch nicht in die zusätzliche Abhängigkeitsliste aufgenommen (festgelegt von: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) Ihrer Projekteinstellung. Jetzt wird der Linker einen LNK2019 melden, da die Definition im aktuellen Suchbereich nicht existiert.


47
2018-03-03 23:52