LNK2019: Verweis auf nicht aufgel÷stes externes Symbol - Hilfe
-
Hallo!
Ich bin noch recht neu in der C++ Welt und mache gerade ein Tutorial, bei dem es darum geht eine template class zu erstellen. Dabei bin ich auf folgendes Problem gestoßen:
Ich versuche eine PriorityQueue zu implementieren, dazu habe ich 3 Dateien:
PriorityQueue.h
PriorityQueue.cpp
Main.cppDiese sehen (verkürzt) folgender maßen aus:
PriorityQueue.h:
#pragma once namespace PQ { template <class T> class PriorityQueue; template <class T> class PriorityQueue { public: PriorityQueue(); ~PriorityQueue(); void add(T val, double key); T extract_min(); T get_min(); double get_min_key(); int getSize(); void output(); private: T* heap_value; double* heap_key; int size; int max_size; void swap(int a, int b); }; }PriorityQueue.cpp:
#include "PriorityQueue.h" #include <iostream> using namespace std; using namespace PQ; template <class T> PriorityQueue<T>::PriorityQueue() { size = 0; heap_value = new T[50]; heap_key = new double[50]; max_size = sizeof(heap_key); } template <class T> PriorityQueue<T>::~PriorityQueue() { delete [] heap_value; delete [] heap_key; } template <class T> void PriorityQueue<T>::add(T val, double key) { // some code here } template <class T> T PriorityQueue<T>::extract_min() { // some code here } template <class T> T PriorityQueue<T>::get_min() { // some code here } template <class T> double PriorityQueue<T>::get_min_key() { // some code here } template <class T> void PriorityQueue<T>::swap(int a, int b) { // some code here } template <class T> int PriorityQueue<T>::getSize() { return size; } template <class T> void PriorityQueue<T>::output() { //some code here }Main.cpp:
#include <iostream> #include "PriorityQueue.h" using namespace PQ; using namespace std; int main() { PriorityQueue<char> p; int x; cin >> x; return 0; }Das Problem ist, dass ich beim compilieren folgende Meldung erhalte:
Fehler 1 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: __thiscall PQ::PriorityQueue<char>::~PriorityQueue<char>(void)" (??1?PriorityQueue@D@PQ@@QAE@XZ)" in Funktion "\_main". Main.obj PriorityQueue Fehler 2 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: \_\_thiscall PQ::PriorityQueue::PriorityQueue (void)" (??0?PriorityQueue@D@PQ@@QAE@XZ)" in Funktion "_main". Main.obj PriorityQueue
Fehler 3 error LNK1120: 2 nicht aufgel÷ste externe Verweise. c:\users\christian\documents\visual studio 2010\Projects\PriorityQueue\Debug\PriorityQueue.exe PriorityQueueAllerdings kann ich alles Problemlos compilieren, wenn ich in der Main.cpp die Zeile mit "PriorityQueue<char> p;" wegnehme. Im Prinzip sieht meine Klasse exakt so aus, wie die im Tutorial (nur dass ich ne PriorityQueue mache und die im Tutorial machen ne Liste). Ich kann mir das überhaupt nicht erklären und hab schon viele Foren durchgoogelt, aber leider noch nichts gefunden.
Ich würde mich freuen wenn jemand eine Idee hat!

Lieben Gruß
Christian
-
Willst Du C++ lernen oder "Visual-C++" (Compiler-Hersteller-spezifisch) --> weg mit #pramga once und setzt 'nen normalen Include-Guard dahin
Du definierst das Klassen-Template in Namensraum PQ (Header), implementierst die Elementfunktionen aber im globane Namensraum --> nicht richtig
Du definierst die Elementfunktionen überhaupt in der cpp-Datei und nicht im Header --> nicht richtig. Wenn Du die in der cpp-Datei versteckst, wird der Compiler die nicht instanziieren können, wenn Du entsprechende Versionen in adneren Übersetzungseinheiten brauchst. ZB wenn du irgendwo die headerdatei einbindest und die queue für T=short benutzen willst. Dann muss der compiler irgendwie die ganzen methoden instantiieren können. das geht nicht, wenn du sie "versteckst". Das Templatezeugs gehört komplett in den header rein.
Du hast Dich nicht an die Dreierregel gehalten. Google nach C++ Dreierregel.
Du machst es Dir unnötig schwer, indem Du selbst mit Zeigern hantierst und Speicher manuell verwalten willst. Das ist gar nicht notwendig. Kannst und solltest Du natürlich zur Übung gemacht haben. Aber Du kannst auch einfach ein dynamisches Feld als Klassenelement benutzen. ZB: std::vector<pair<double,T> > oder so. Dann erübrigt sich das mit der 3er-Regel.
kk
-
Vielen Vielen Dank für die Tipps! Ich habe nun folgendes gemacht:
1. #pragma once weggemacht und das hier eingefügt:
#ifndef PRIORITYQUEUE_H #define PRIORITYQUEUE_H // code here #endif2. Die PriorityQueue.cpp gelöscht und den Inhalt in die .h gepackt (unter die Klassendefinition.
3. Einen copy-konstruktor implementiert. Habe nach der 3-er regel gegoogelt und dabei folgendes gefunden:
-Kopierkonstruktor
-Konstruktor/Destruktor
-ZuweisungsoperatorIch verstehe allerdings nicht ganz was es mit dem Zuweisungsoperator auf sich hat.
Außerdem hab ich auch noch nicht so ganz verstanden, was du mit speicher manuell verwalten meinst. Meinst du etwa, ich sollte die beiden arrays (heap_value und heap_key) als vectoren implementieren, damit sie sich automatisch vergrößern? Mein momentaner array-vergrößerungs-code sieht nämlich so aus (und funktioniert!
):if(size == max_size) { // double the size of the arrays T* new_heap_value = new T[max_size * 2]; double* new_heap_key = new double[max_size * 2]; for(int i = 0; i < size; i++) { new_heap_value[i] = heap_value[i]; new_heap_key[i] = heap_key[i]; } delete [] heap_value; delete [] heap_key; heap_value = new_heap_value; heap_key = new_heap_key; max_size = max_size * 2; }Ist das so richtig?
Vielen Dank nochmal, es funktioniert jetzt.
Christian
PS: ne will schon c++ lernen, nicht visual-c++.
-
christian_bb schrieb:
3. Einen copy-konstruktor implementiert. Habe nach der 3-er regel gegoogelt und dabei folgendes gefunden:
-Kopierkonstruktor
-Konstruktor/Destruktor
-ZuweisungsoperatorIch verstehe allerdings nicht ganz was es mit dem Zuweisungsoperator auf sich hat.
Der ist für's Kopieren zuständig, wenn's schon ein Objekt gibt.
int i = 3; int j = i; // Kopier-Initialisierung j = i; // ZuweisungMit einem Klassentyp statt int würde das erste über den Kopierkonstruktor laufen und das zweite über den Zuweisungsoperator.
christian_bb schrieb:
Außerdem hab ich auch noch nicht so ganz verstanden, was du mit speicher manuell verwalten meinst. Meinst du etwa, ich sollte die beiden arrays (heap_value und heap_key) als vectoren implementieren, damit sie sich automatisch vergrößern?
Ich will Dir da nichts vorschreiben. Aber ja, so könnte man es machen, ohne sich weiter Sorgen machen zu müssen.
christian_bb schrieb:
Mein momentaner array-vergrößerungs-code sieht nämlich so aus (und funktioniert!
):if(size == max_size) { // double the size of the arrays T* new_heap_value = new T[max_size * 2]; double* new_heap_key = new double[max_size * 2]; for(int i = 0; i < size; i++) { new_heap_value[i] = heap_value[i]; new_heap_key[i] = heap_key[i]; } delete [] heap_value; delete [] heap_key; heap_value = new_heap_value; heap_key = new_heap_key; max_size = max_size * 2; }Ist das so richtig?
Es ist nicht 100%ig Ausnahme-sicher. Du machst hier zu viele Dinge auf einmal. Die ganze Logik/Verwaltung für ein dynamisches Array lässt sich schön "verpacken". Und das ist das, was bei std::vector schon passiert ist. Aber eigentlich brauchst Du auch keine PriorityQueue mit einem vector bauen. Eine PriorityQueue gibt es in der Standardbibliothek ja schon.
Ein anderes beliebtes Beispiel: Matrix.
Blöd:class matrix { double* elements; int rows, cols; public: matrix(matrix const& m); ~matrix(); void swap(matrix & with); matrix& operator=(matrix kopie) { this->swap(kopie); return *this; } ... };Schlau:
class matrix { vector<double> elements; int rows, cols; public: ... };(Kein benutzerdefinierter Kopierkonstruktor, Destruktor oder Zuweisungsoperator nötig)
kk
-
Ich danke dir vielmals für diese ausführliche Erklärung, hab es nun verstanden!

Lieben Gruß,
Christian