2009-05-07 32 views
0

Ich Verknüpfung Fehler des folgenden Typs erhalten:Wurf Beifang Ursache Verknüpfung Fehler

Festival.obj: error LNK2019: nicht aufgelöstes externes Symbol „public: Leere __thiscall Baum :: add (Klasse Preis &)“ (? hinzufügen @? $ Baum @ vPrice @@@@ QAEXAAVPrice @@@ Z) in Funktion verwiesen __catch $? AddBand @ Festival @@ QAE? AW4StatusType @@ HHH @ Z 0

$

Ich dachte immer, es hat mit Try-Catch-Mechanis zu tun m, aber seitdem anders erzählt worden. Dies ist eine aktualisierte Version der Frage.

Ich verwende Visual Studio 2008, aber ich habe ähnliche Probleme in g ++.

Der entsprechende Code:

In Festival.cpp

#include "Tree.h" 
#include <exception> 

using namespace std; 

class Band{ 
public: 
    Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){}; 
... 
private: 
... 

}; 

class Festival{ 
public: 
    Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){}; 
    ~Festival(); 
    StatusType AddBand(int bandID, int price, int votes=0); 
    ... 

private: 
    Tree<Band> bandTree; 
    ... 

}; 

StatusType Festival::AddBand(int bandID, int price, int votes){ 
    if ((price<0)||(bandID<0)){ 
     return INVALID_INPUT; 
    } 
    Band* newBand=NULL; 
    try{ 
     newBand=new Band(bandID,price-priceOffset,votes); 
    } 
    catch(bad_alloc&){return ALLOCATION_ERROR;} 
    if (bandTree.find(*newBand)!=NULL){ 
     delete newBand; 
     return FAILURE; 
    } 
bandTree.add(*newBand); 
.... 
} 

In Tree.h:

template<class T> 
class Tree{ 
public: 
    Tree(T* initialData=NULL, Tree<T>* initialFather=NULL); 
    void add(T& newData); 
.... 
private: 
.... 
}; 

Interessanterweise habe ich nicht Verknüpfung Fehler, wenn ich versuche Baum-Funktionen zu nutzen wenn Typ T ein primitiver Typ wie ein int ist.

Antwort

2

Gibt es Tree.cpp? Wenn ja, hast du vielleicht vergessen es zu verlinken? Wo ist die Implementierung von Tree :: add? Außerdem sehe ich nicht, wo Sie Baum :: hinzufügen aufrufen. Ich denke, es sollte in der Versuchserklärung sein, direkt nach dem neuen?

Nur zur Erinnerung:

Für die meisten Compiler (das heißt solche, die getrennte Sammlung üben) die Umsetzung der Elementfunktionen einer Template-Klasse hat bei der Erstellung der Quelldatei sichtbar sein, der die Vorlage-Klasse verwendet. Normalerweise folgen Benutzer dieser Regel, indem sie die Implementierung der Member-Funktionen in die Header-Datei einfügen.

Vielleicht Tree :: hinzufügen ist nicht in der Kopfzeile? Dann besteht eine mögliche Lösung im besprochenen Fall darin, die Implementierung von Tree :: in die Header-Datei einzufügen.

Der Unterschied zwischen regulären Klassen und Vorlagenklassen besteht, weil Vorlagenklassen keine "echten" Klassen sind - es ist, na ja, eine Vorlage. Wenn Sie Ihre Tree-Klasse als reguläre Klasse definiert hätten, könnte der Compiler Ihren Code sofort verwendet haben. Im Falle einer Vorlage "schreibt" der Compiler zuerst die reale Klasse für Sie und ersetzt die Template-Parameter durch die von Ihnen bereitgestellten Typen. Jetzt kompiliert der Compiler cpp-Dateien einzeln. Er kennt keine anderen cpp-Dateien und kann nichts aus anderen cpp-Dateien verwenden.Lassen Sie uns sagen, dass Ihre Implementierung von Baum: add sieht wie folgt aus:

void Tree::add(T& newData) 
{ 
    newData.destroyEverything(); 
} 

es völlig legitim ist, solange Ihr T-Methode destroyEverything hat. Wenn der Compiler Class.cpp kompiliert, möchte er sicher sein, dass Sie nichts tun, was T nicht weiß. Zum Beispiel Tree<int> wird nicht funktionieren, weil Int nicht alles zerstören. Der Compiler wird versuchen, Ihren Code mit int anstelle von T zu schreiben und herauszufinden, dass der Code nicht kompiliert wird. Aber da der Compiler nur die aktuelle cpp und alles, was er enthält, "sieht", kann er die add-Funktion nicht validieren, da er sich in einer separaten cpp befindet.

Es wird kein Problem sein, mit

void Tree::add(int& newData) 
{ 
    newData.destroyEverything(); 
} 

in einem separaten cpp implementiert, da der Compiler weiß, dass int die einzig akzeptable Art und kann „auf sich selbst zählen“, wenn er bekommt Baum zu kompilieren. cpp er wird den Fehler finden.

+0

Hinzugefügt die Verwendung von Tree :: add, es ist direkt am Ende des Festival-Segments. Ja, es gibt Tree.cpp, und es # enthält Tree.h " –

+0

Tree hinzufügen ist in der Tat nicht in der Kopfzeile. Es ist in Tree.cpp, die # Tree.h enthält. Können Sie erklären, warum diese Art von Sache (Schreiben Klasse .h, Class.cpp, und dann #incliude "Class.h" verwenden) ist für keine Template-Klassen/Funktionen in Ordnung, aber funktioniert nicht mit Template-Funktionen? Was macht es nicht sichtbar? –

+0

Fast alle vorhandenen C++ - Compiler erfordern die beiden Deklaration und die Definition von Templates, die vom Compiler zu sehen sind - dh es gibt keine Verknüpfungsstufe für Templates. Sie müssen die Funktionsdefinitionen in die Header-Datei schreiben. –

0

Sind Sie sicher, dass die try/catch irgendetwas damit zu tun hat? Was passiert, wenn Sie einfach die Zeilen try und catch auskommentieren, den Rest des Codes so lassen, wie er ist, und diesen Code erstellen?

Es kann nur sein, dass Sie die Bibliothek, die Tree::add(class Price &) von Ihrer Verbindungslinie definiert fehlt.

+0

Ich habe versucht, sie auskommentieren und die Anzahl der ungelösten Symbole ist immer noch gleich. Ich nehme an, es hat mit try-catch zu tun, weil es besagt, dass es sich um eine Funktion handelt, auf die in ___catch verwiesen wird, und das ist meine beste Vermutung. –

+0

Der Verweis auf __catch im Fehler ist irrelevant (es ist ein Nebeneffekt der Art, wie der Compiler Exception-Handler kompiliert). Ihr Problem ist einfach, dass Sie nicht mit der Bibliothek verknüpfen, die Tree :: add (Klasse Price &) definiert. – RichieHindle

+0

Ich dachte darüber nach, aber die Funktion ist Teil von Tree.h, und ich schließe sie ein. Die definierte Funktion ist: Vorlage void Baum :: hinzufügen (T & newData); Wir nennen es so: priceTree.hinzufügen (* newPriceNode); während priceTree Baum ist, die beide in der betreffenden cpp-Datei definiert sind. –

0

Update: die Verwendung von Tree-Funktionen mit einem primitiven Typ führt nicht zu einem Verknüpfungsfehler. Ich aktualisierte meine Frage angesichts einiger der Dinge, die gesagt wurden.

0

Sie erhalten Verknüpfungsfehler, keine Compilerfehler. Dies sagt uns, dass der Compiler wusste, welche Art von Funktion Tree::add() ist, hatte aber keine Definition. In Tree.h sehe ich eine Deklaration der Funktion add(), aber keine Definition. Es sieht komisch aus; Weiß jemand woher Tree.h kam?

Normalerweise kommt eine Template-Klasse mit Memberfunktionsdefinitionen in der Include-Datei, da die Funktionen irgendwo instanziiert werden müssen, und der Compiler instanziiert, wenn er verwendet wird und vom Linker sortiert werden kann. Wenn die Definitionen in Tree.h sind, würde ich erwarten, dass alles wie geplant funktioniert.

Also werde ich auf ein Bein und gehe davon aus, dass die Definitionen in einer separaten Datei, nicht verknüpft sind, und dass es anderswo Bestimmungen für die Instanziierung für grundlegende Typen wie Tree<int> gibt. Dies ist vermutlich, um die Kompilierung zu rationalisieren, da diese Dinge normalerweise an mehreren Stellen kompiliert werden, und das braucht Zeit.

Was Sie in diesem Fall tun müssen, ist zu finden, wo Tree<int> instanziiert wird, und fügen Sie eine Instanziierung für Ihre Klasse hinzu.

Ich könnte hier weit weg sein, aber meine Erklärung passt zu den Fakten, die Sie gegeben haben.

bearbeitet nach dem ersten Kommentar:

Vorlagen sind etwas komplizierter als gewöhnliche Funktionen, die in der Regel kein wirkliches Problem ist. Wenn die Definitionen für alle Aufrufe in Tree.h wären, könnte Festival.cpp Tree<Band> instanziieren und alles wäre cool. Das ist die übliche Technik, und Sie stoßen auf dieses Problem, weil Sie es nicht verwenden.

Wenn Sie eine Funktion schreiben, wird sie kompiliert und der Linker findet sie. Jede Routine, die diese Funktion aufruft, muss den Funktionsprototyp kennen, damit sie weiß, wie sie aufgerufen wird. Wenn Sie eine Vorlage schreiben, schreiben Sie nichts, was direkt in das Programm geht, aber jede Verwendung der Vorlage gilt als Schreiben aller Funktionen.

Daher muss irgendwo in Ihrem Programm irgendwo Tree<Band> verwendet werden, damit eine Tree<Band>::add() Funktion kompiliert werden kann. Die Definition von Tree<T>::add muss dem Compiler zur Verfügung stehen, wenn Tree<Band> instanziiert wird, da der Compiler sonst keine Idee hat, was er kompilieren soll. In diesem Fall wird der Funktionsaufruf generiert, und Sie können sicher sein, dass die Funktion an anderer Stelle kompiliert wird.

Daher müssen Sie Tree<Band> in einer Datei instanziieren, die Zugriff auf beide Definitionen für Tree<T> und Band hat. Dies bedeutet wahrscheinlich eine Datei, die Tree.cpp enthält oder enthält und Festival.h enthält.

Der Linker verwendet bereits Tree.cpp, aber Tree.cpp hat Tree<Band> nicht definiert, so dass es für den Linker bedeutungslos ist. Vorlagen sind nur für den Compiler nützlich, und der Linker arbeitet nur mit dem, was der Compiler aus Vorlagen generiert hat.

Der schnelle Weg, dies zu lösen, ist, die Definitionen von Tree.cpp zu nehmen und sie in Tree.h. Das wird leider die Kompilierungs- und Verbindungszeiten erhöhen. Die andere Technik besteht darin, alle Vorlagenverwendungen in Tree.cpp zu instanziieren, sodass sie dort kompiliert werden.

+0

Ich habe eine Tree.cpp-Datei, die Tree.h. das ist genug für den Linker? So etwas reicht normalerweise für normale Funktionen, aber ich sehe jetzt, dass Vorlagen schwieriger sind. Übrigens, ich habe versucht, Klasseexplizit instanziieren 10 in der Datei Festival.cpp und es antwortete "keine geeignete Definition für explizite Vorlage Instanziierungsanforderung" –

+0

auch, wenn es nicht genug für den Linker, ich denke, ich kann versuchen, die Implementierung in die Header-Datei verschieben, aber ist da eine Möglichkeit, den Linker auf die cpp-Datei zu verweisen? –

0

Wie andere gesagt haben, müssen Sie die Implementierung von Treee :: add() zeigen und uns sagen, wie Sie es verknüpfen.

auf einem nicht verwandten Punkt, wenn Sie Konstrukte verwenden wie:

Band* newBand=NULL; 
    try{ 
     newBand=new Band(bandID,price-priceOffset,votes); 
    } 
    catch(bad_alloc&){return ALLOCATION_ERROR;} 

im gesamten Code, verschwenden Sie Ihre Zeit offen. Die Chancen, dass Sie in einem modernen Betriebssystem zu einer Erschöpfung der Erinnerungen kommen, sind gering und die Chancen, dass Sie etwas Nützliches tun, nachdem es passiert ist, sind ungefähr null. Sie werden viel besser dran, einfach sagen:

Band * newBand = new Band (bandID, price - priceOffset, votes); 

ot möglicherweise:

Band newBand(bandID, price - priceOffset, votes); 

und die Ausnahme in diesem Fall der Handhabung zu vergessen.

+0

Ich weiß. Dies ist für einen Hochschulauftrag. –

+0

Sie schreiben also bewusst sinnlosen Code dafür? –

+0

Sie bitten uns, Speicherereignisse zu behandeln. –

0

schrieb Sie in einem Kommentar:

Ich dachte darüber nach, aber die Funktion ist Teil Tree.h, und ich sie gehören. Die definierte Funktion ist: Template void Tree :: add (T & newData); Wir nennen es den folgenden Weg: priceTree.add (* newPriceNode); während priceTree Tree ist, die beide in der betreffenden cpp-Datei definiert sind.

statt:

priceTree.add(*newPriceNode); 

Versuch:

priceTree.add(newPriceNode); //no "*" before "newPriceNode" 

add() nimmt einen Verweis auf einen Knoten, kein Zeiger auf einen Knoten (entsprechend Ihrer Definition von Baum).

Verwandte Themen