undefined reference to function
-
Hallo,
ich bekomme in Eclipse den Fehler "undefined reference to myfunction" und weiß nicht woran das liegt.
Mit Code posten ist ein bissl schwierig, weil's mehrere Klassen mit Headern sind.Ich versuche mal die Structur zu erläutern (es soll ein Programm zum Laden von Bildern werden):
+Testfile - test.cpp (main-funktion) +Header - Image.h - Bmp.h - ImageLoader.h - BmpLoader.h +Implementation - Bmp.cpp - BmpLoader.cpp
Bmp erbt (public) von Image und BmpLoader von ImageLoader.
In Imageloader gibt's die Funktion
virtual Image* loadImage(string file) = 0;
Diese wird in BmpLoader überschrieben:
Bmp* loadImage(string file);
(sollte gehn weil Bmp Unterklasse von Image, oder?)
Wenn ich jetzt in test.cpp in der main
BmpLoader bmpl; Bmp* img; img = bmpl.loadImage("../testbild.bmp");
loadImage aufrufe, dann bekomm ich "undefined reference to loadImage".
Das Seltsame: bei der Autovervollständigung in Eclipse findet er die Funktion, beim Compilieren/Linken nicht mehr!Ich hab jetzt schon google gewälzt und eine vereinfachte Struktur neu gecodet, aber ich hab keine Ahnung woran es liegt
Kann mir jemand weiterhelfen? Thx!
-
Edit: Ok, sofern
Bmp
vonImage
erbt.Hat
loadImage()
auch eine Funktionsdefinition?
-
in ImageLoader nicht (da ist es ja pure virtual)
in BmpLoader schon (Deklaration in ".h", Definition in ".cpp")
-
Hat jemand eine Idee wo man noch suchen könnte/nachschauen könnte?
Thx
-
Poste mal ein Minimalbeispiel das nicht läuft.
-
Schaut so aus als wäre bmp.cpp nicht in deinem Projekt. Überprüfe mal ob er alle drei cpp-Dateien compiliert und alle drei entstehenden .o-Dateien auch an den Linker übergibt. Wenn das der Fall ist liefere uns wie Bernd schon sagt ein compilierbares Minimal-Beispiel (also keine überflüssigen Zeilen), das den selben Fehler produziert.
-
also wenn in der main nur ein cout und die includes stehen, dann compiliert alles und es lässt sich starten (Ausgabe erscheint auf der Konsole)
EDIT: Beispiel entfernt
Minimalbeispiel: working on it...
-
keine Ideen mehr wo der Fehler liegen könnte? Ich bin nämlich ziemlich ratlos mittlerweile... Hab schon so einiges versucht
-
pumuckl schrieb:
liefere uns wie Bernd schon sagt ein compilierbares Minimal-Beispiel
Was du uns da geliefert hast ist nicht compilierbar.
-
Sowas bitte:
class Image { public: virtual ~Image(){}; }; class Bmp : public Image {}; class ImageLoader { public: virtual Image* LoadImage() = 0; }; class BmpLoader : public ImageLoader { public: Bmp * LoadImage() { return new Bmp; } }; int main(int argc, char* argv[]) { BmpLoader l; Bmp * img = l.LoadImage(); delete img; return 0; }
-
ok hier mal ein Minimalbeispiel - theoretisch kompilierbar (praktisch: Fehler!)
ich hätt's ja als Datei angehängt, aber ich finde hier keine Option dafür
#include <iostream> using std::cout; using std::endl; class Image{ protected: long _height; long _width; char* _pixels; public: Image(){} Image(unsigned int width, unsigned int height, char* data); virtual ~Image(); virtual char* getPixels() const = 0; virtual long getHeight() const = 0; virtual long getWidth() const = 0; }; class ImageLoader{ public: virtual Image* loadImage(string file)= 0; template <typename T> static T transformLittleEndian(int i){ return new T(); } }; class Bmp : public Image{ public: Bmp(unsigned int width, unsigned int height, char* data) : _width(width), _height(height), _pixels(data) {} ~Bmp(){ delete[] _pixels; } char* getPixels() const{ return _pixels; } long getHeight() const{ return _height; } long getWidth() const{ return _width; } }; class BmpLoader : public ImageLoader{ public: Bmp* loadImage(string file){ cout << "loadimage: " << file << endl; char* data = new char[10]; return new Bmp(1, 1, data); } }; int main(){ cout << "Launching test..." << endl; BmpLoader bmpl; Bmp* img; img = bmpl.loadImage("../testbild.bmp"); //FIXME: crash cout << img->getHeight() << endl; return 0; }
Bin für jede Hilfe dankbar
-
Da war ja noch einiges im Argen. string nicht includet, Bmp per Elementinitialisierer die Member der Basisklasse initialisiert, image mit virtuellem Destruktor OHNE Definition...
So kompiliert und linkt und läuft es hier:#include <iostream> #include <string> using std::cout; using std::string; using std::endl; class Image { protected: long _height; long _width; char* _pixels; public: Image(){} Image(unsigned int width, unsigned int height, char* data) : _height(height), _width(width), _pixels(data) {} virtual ~Image() {}; virtual char* getPixels() const = 0; virtual long getHeight() const = 0; virtual long getWidth() const = 0; }; class ImageLoader { public: virtual Image* loadImage(string file) const = 0; template <typename T> static T transformLittleEndian(int i){ return new T(); } }; class Bmp : public Image{ public: Bmp(unsigned int width, unsigned int height, char* data) : Image(width, height, data) {} ~Bmp(){ delete[] _pixels; } char* getPixels() const{ return _pixels; } long getHeight() const{ return _height; } long getWidth() const{ return _width; } }; class BmpLoader : public ImageLoader{ public: Bmp* loadImage(string file) const { cout << "loadimage: " << file << endl; char* data = new char[10]; return new Bmp(1, 1, data); } }; int main(){ cout << "Launching test..." << endl; BmpLoader bmpl; Bmp* img; img = bmpl.loadImage("../testbild.bmp"); //FIXME: crash cout << img->getHeight() << endl; return 0; }
Wobei es nicht schön ist, wenn die Basisklasse die Daten hält, die abgeleitete Klasse diese dann aber zerstört.
Auch könnten die getter direkt nach Image wandern, ist doch doof, wenn die selbe Implementierung in jeder angeleiteten Klasse stehen muss. Ich würde da die Klassenhierarchie und -Aufteilung nochmal überdenken. Reicht es nicht, wenn du eine Klasse "Image" für die Daten hast, und nur verschiedene Loader/Exporter anbietest? Ich sehe jetzt nicht, wie sich ein Bmp gegenüber einem Image im Programm unterscheiden wird, nur die Repräsentation auf der Festplatte ist anders.
-
hey, super, VIELEN DANK!!!
Ok, das mit dem string ist wohl beim Kopieren auf der Strecke geblieben...
Wäre es dann besser, pixels im virtuellen Destruktor der Basisklasse zu zerstören (wird der implizit schon aufgerufen?) und die getters hochzuziehen?
(dann steht zwar in Bmp nichts mehr drin, aber die Klassenstruktur ist auch für weitere Imagefiles gedacht, die evtl Zusatzattribute haben ;))
-
Noch eine Frage: warum hast du loadImage const gemacht? (und warum funktioniert's nicht auch ohne?)
-
</Exit> schrieb:
Noch eine Frage: warum hast du loadImage const gemacht? (und warum funktioniert's nicht auch ohne?)
Weil loadImage das ImageLoader-Objekt nicht verändert, und deshalb als const deklariert werden sollte. Wenn das aber in deinem endgültigen Code NICHT zutreffen sollte (also loadImage auch Member von ImageLoader verändert ooder zwingend notwengie nicht-const-Methoden aufruft), musst du natürlich das const wieder wegmachen, sonst gibt es Compiler-Errors.
Google mal nach "const correctness".
-
</Exit> schrieb:
Wäre es dann besser, pixels im virtuellen Destruktor der Basisklasse zu zerstören (wird der implizit schon aufgerufen?) und die getters hochzuziehen?
(dann steht zwar in Bmp nichts mehr drin, aber die Klassenstruktur ist auch für weitere Imagefiles gedacht, die evtl Zusatzattribute haben ;))
Wenn es nicht das Normale ist, dass ein Bild Zusatzinfos hat, würde ich nicht auf eine Klassenhierarchie bauen, sondern über "
bool Image::hasAdditionalInfo() const
" und (z.B.) "std::map<std::string, std::string> Image::getAdditionalInfo() const
" regeln. Aber auch sonst - denk nurdaran, dass man in den meisten Fällen mit einem Image-Objekt arbeiten wird, um alle möglichen Subklassen unterstützen zu können (Polymorphie eben), nur um bei den zusätzlichen Infos, die dann am Ende auch noch unterschiedlich heißen sollen, in alle möglichen Richtungen casten zu müssen?
Vor allem der Umstand, dass du leere (!) Image-Derivate erstellst, nur um deinem selbst gesetzten System treu zu bleiben, ist mehr als fraglich
-
const correctness... da sind wir ja bei einem meiner Lieblingsthemen!
Das mit dem getAdditionalInfo war auch der erste Versuch (zuerst allerdings mit einem struct), dabei ist dann aber so viel schief gegangen, dass es dann doch eine Vererbungshierarchie wurde
Jetzt muss ich nur noch das zum laufen bringen... (das Minimalbeispiel läuft, aber das richtige noch nicht)
Danke aber soweit, jetzt muss ich nur schauen, dass ich wirklich alles ausgebessert bekomme
-
Aaaah, hab endlich den Fehler gefunden der für "undefined reference" verantwortlich war. (Danke trotzdem für die Korrekturen der anderen Fehler!)
Wen's interessiert: ich habe Eclipse benutzt und es wurden die Header (die im extra Ordner "Header" im Projektverzeichnis sind) nicht (dazugelinkt) EDIT: "eingebunden".
Abhilfe bietet (unter Eclipse):
Project -> Properties -> C++ Build -> Settings -> GCC C++ Compiler -> Directories und dann den entsprechenden Ordner adden und
Project -> Properties -> C++ General -> Paths & Symbols -> Library Paths
und wieder den Ordner adden
-
</Exit> schrieb:
ich habe Eclipse benutzt und es wurden die Header (die im extra Ordner "Header" im Projektverzeichnis sind) nicht dazugelinkt.
Header werden niemals dazugelinkt. Header werden vom Prä-Prozessor eingebunden... Und wenn er die Dateien nicht findet, dann gibt er lauthals eine Warnung/Fehler aus.
-
"undefined reference" ist ja so ein toller Fehler
- No comment!
-
</Exit> schrieb:
"undefined reference" ist ja so ein toller Fehler
- No comment!
Eigentlich steht da auch "undefined reference to <function>", woraus man genauere Informationen über die Funktion bekommt (sofern man die Fehlermeldung liest).
Falls du dich nur am "undefined reference" störst, das bezeichnet eben eine Funktion, die im Code referenziert wurde, aber nirgends definiert ist. Also eine ziemlich treffende Beschreibung. Nachdem du den Linkerfehler einmal gehabt hast, ist dir auch unmittelbar klar, worum es geht.