Beim Linken die Fehlermeldung 'undefined reference to'
-
Hallo ihr Lieben,
wieder mal eine Frage. Ich habe versucht das hier gelernt und angeratene in die Tat umzusetzen und verwende für meine Funktion nun eine Klasse. Zunächst in einer Header Datei die Deklaration:
// ionization_cs.h #ifndef IONIZATION_CS_H #define IONIZATION_CS_H class IonizationCS { public: IonizationCS(double ionization_potential); // constructor ~IonizationCS(); // destructor double get_cs_max(double T); private: static double A1, A2, B1, B2, a0, N, Ry; double S, F1, F2, I; }; #endifDa ich nicht davon ausgehe, dass dieses Objekt kopiert wird, habe ich bisher auf einen Copy- und Assignment Constructor verzichtet (obwohl ich das Kapitel im C++ Primer gelesen habe, ich denke die Problematik kommt später).
Dann die Methoden brav in einer cpp Datai definiert
// ionization_cs.pp -- IonizationCS class methods #include<iostream> #include<cmath> #include "ionization_cs.h" using namespace std; // initializing static class members double IonizationCS::A1 = 0.94; double IonizationCS::A2 = 1.13; double IonizationCS::B1 = 2.3; double IonizationCS::B2 = 22.0; double IonizationCS::a0 = 5.3e-9; // Bohrradius [a0] = cm double IonizationCS::Ry = 13.6; // Rydberg energy [Ry] = eV double IonizationCS::N = 2; // number of electrons per shell // class methods // constructor IonizationCS::IonizationCS(double ionization_potential) { I = ionization_potential; S = 4 * M_PI * pow(a0,2)*pow(Ry/I,2); } // destructor IonizationCS::~IonizationCS(){} // get the maximal ionization cross section, simply depending on the energy of the primary electron, // thus the energy of the secondary electron is W = T - I // [sigma] = cm^2 inline double IonizationCS::get_cs_max(double T) { const double t = T/I; F1 = A1 * log(t)/(t + B1); F2 = A2 / (t + B2); return // S * F1 * (t-1)/(2*pow(t,2)) * (t + 1 - (4 * sqrt(t))/(3 + t)) // + S * F2 * (1 - 1/t - log(t)/(t+1)); }Jetzt will ich das ganze natürlich nutzen:
// ionization_cs_plot.cpp #include <iostream> #include <stdio.h> #include <fstream> // personal header files #include "ionization_cs.h" using namespace std; int main(){// double T = 80; double I = 12.6; class IonizationCS* p_cs = new IonizationCS(I); cout << p_cs->get_cs_max(T) << endl; delete p_cs; return 0; }Das Kompilieren klappt auch wunderbar und nur beim Linken kracht es dann:
g++ -Wall -pedantic -ansi -c ionization_cs.cpp g++ -Wall -pedantic -ansi -c ionization_cs_plot.cpp g++ -o "ionization_cs_plot" ionization_cs.o ionization_cs_plot.o ionization_cs_plot.o: In function `main': ionization_cs_plot.cpp:(.text+0x75): undefined reference to `IonizationCS::get_cs_max(double)' collect2: ld returned 1 exit statusDer Syntax mit dem Pointer sollte doch richtig sein, also das Zugreifen auf eine Klassenfunktion mittels ->. Wieso kracht es dann beim Linken? Die Klassenfunktion ist doch richtig definiert, oder nicht? Mittels inline (habe es auch mal ohne probiert, hat nix gebracht) und mittels Klassenzugehörigkeit IonizationCS::get_cs_max(double T). Ich würde ja irgendwie naiv auf einen Tippfehler tippen, aber dann hätte doch schon das Kompilieren von ionization_cs.cpp eine Fehlermeldung ausgeben müssen - oder nicht?
Oder sehe ich gerade den Wald vor lauter Bäumen nicht?

Gruß,
Klaus.
-
Klaus82 schrieb:
Der Syntax mit dem Pointer sollte doch richtig sein, also das Zugreifen auf eine Klassenfunktion mittels ->. Wieso kracht es dann beim Linken? Die Klassenfunktion ist doch richtig definiert, oder nicht?
Nö, sie ist inline und hat daher interne Bindung. Das ist sogar der einzig sinnvolle Einsatz von inline, wenn man dies erreichen möchte. Das was du dir hier erhoffst(Inlining der funktion), macht ein moderner Compiler automatisch, sofern er kann (und hier kann er nicht, sofern du nicht zur Linkzeit optimieren lässt)
Mittels inline (habe es auch mal ohne probiert, hat nix gebracht)
Huh? Sicher, dass du sonst alles richtig gemacht hast?
-
Das passiert weil deine Funktion inline Schlüsselwort davor hat. Das bedeutet die wird direkt in den Code kopiert und nicht aufgerufen. Dafür muss ihre Definition für die aufrufende Einheiten sichtbar sein.
P.S. So eine würde ich nie inlinen.. Die ist zu groß und kompliziert.
-
Travor schrieb:
Das bedeutet die wird direkt in den Code kopiert und nicht aufgerufen.
Falsch. Das bedeutet, dass dem Compiler mitgeteilt wird, dass er schauen soll, ob es sich lohnt, die Funktion inline zu machen. Er darf das aber auch gerne ignorieren.
-
Inliner schrieb:
Travor schrieb:
Das bedeutet die wird direkt in den Code kopiert und nicht aufgerufen.
Falsch. Das bedeutet, dass dem Compiler mitgeteilt wird, dass er schauen soll, ob es sich lohnt, die Funktion inline zu machen. Er darf das aber auch gerne ignorieren.
Ich bleibe dabei, das ich Recht habe.
-
*dass
Du liegst falsch.
-
SeppJ schrieb:
Klaus82 schrieb:
Mittels inline (habe es auch mal ohne probiert, hat nix gebracht)
Huh? Sicher, dass du sonst alles richtig gemacht hast?
Verdammt, scheinbar nicht. Hab eben das inline mal weggenommen und jetzt funktioniert es. Dann muss vorher beim Probieren etwas verdummbeutelt haben. Sorry!

Puh, bei der Verwendung von inline bin ich jetzt aber auch irritiert.
Also prinzipiell kann ich ja eine Klassenfunktion in der Deklaration definieren (und zunächst deklarieren) oder nicht.
Also die beiden folgenden Beispiele sind gleich:class Name{ double any_function(){ return something; } }; // --- ODER --- className{ double any_function(); }; double className::any_function() { return something; }Das geht auf jeden Fall und hat noch nichts mit inline zu tun.
Jetzt steht hier aber z.B. im C++ Primer
The easiest way to make sure that inline definitions are avaible to all files in a multifile programm is to include the inline definition in the same header file in which the corresponding class is defiened.
Mag zwar der einfachste Weg sein, doch nachdem was ich hier gelernt habe, sollen Definition nicht in header files, sondern nur Deklarationen. Also vergesse ich das schnell wieder.
Aber weiterhin steht noch
Incidentally, according to the rewrite rules, defining a method in a class declaration is equivalent to replacing the method definition with prototyp and then rewriting the definition as an inline function immediatly after the class declaration.
Also warum geht es dann nicht?
Gruß,
Klaus.
-
Also in erster Linie heißt Inline erst einmal, dass die Methode sich so verhält, als hätte sie interne Bindung (die genauen Regeln sind leicht anders). Also ungefähr so, als wäre sie static.
Die Sache mit dem Einbauen der Methode in den Code ist eher ein Hinweis an den Compiler, den dieser sowieso ignoriert. Wenn der Compiler einen Funktionsaufruf sieht und den Code dieser Funktion sieht, dann macht er sich ohnehin ein eigenes Bild, inline oder nicht. Wenn er den Code nicht sehen kann, kann er ohnehin nichts inlinen, egal ob die Funktion inline ist oder nicht. Denn er muss ja den Code haben, um ihn einbauen zu können.Daher:
Eine Inlinefunktion in einem getrennten Modul ist komplett sinnlos. Das ist das was du hier hattest. Der Compiler sieht nicht den Code während der Übersetzung der Module die die Funktion benutzen und kann die Funktion daher nicht inlinen. Und beim Linken bekommt man einen Fehler, weil die Funktion nicht sichtbar ist.Wie bekommt man eine Funktion nun Inline (im Sinne des Codeeinbauens)?
Wenn man sie in einem anderen Modul hat, hat man, wie erklärt, sowieso schlechte Karten. Neuerdings bieten viele Compiler auch Optimierung zur Linkzeit, dann geht das auch. Aber so ganz ausgereift würde ich die Technik noch nicht nennen, insbesondere muss man sich gut mit dem Buildprozess auskennen, um alles richtig zu machen.
Methoden die Inline werden sollen, müssen daher stets sichtbar sein. Wo sind sie das? Im Header! Also im Header definieren. Aber oh weh! Das gibt mehrfache Definitionen und Probleme beim Linken. Die Methode darf beim Linken also nicht sichtbar sein, muss interne Bindung haben. Und nun schließt sich der Kreis: Man macht die Methode im Header inline. Oh, Wunder! Die Sprache scheint wohl doch ganz gut durchdacht zu sein
.Mag zwar der einfachste Weg sein, doch nachdem was ich hier gelernt habe, sollen Definition nicht in header files, sondern nur Deklarationen. Also vergesse ich das schnell wieder.
Dann hast du das entweder falsch verstanden oder jemand hat es falsch erklärt ,der es vorher falsch verstanden hat. Hier gibt es öfters schlechte Tipps von Leuten, die selber gute Tipps bekommen haben, aber diese nicht ganz verstanden haben und sie nun übereifrig an andere weitergeben, obwohl sie an der Stelle gar nicht passen.
Ich schreibe fast alle kurzen Methoden in den Header und die Ausführungsgeschwindigkeit meiner Programme dankt mir.
-
Hi,
SeppJ schrieb:
Methoden die Inline werden sollen, müssen daher stets sichtbar sein. Wo sind sie das? Im Header! Also im Header definieren. Aber oh weh! Das gibt mehrfache Definitionen und Probleme beim Linken. Die Methode darf beim Linken also nicht sichtbar sein, muss interne Bindung haben. Und nun schließt sich der Kreis: Man macht die Methode im Header inline. Oh, Wunder! Die Sprache scheint wohl doch ganz gut durchdacht zu sein
.Ich schreibe fast alle kurzen Methoden in den Header und die Ausführungsgeschwindigkeit meiner Programme dankt mir.
Okay,
also mache ich anstatt einer separaten header und cpp Datei eine Header Datei, in der mittels inline die Methoden schon drinstehen?// ionization_cs.h #ifndef IONIZATION_CS_H #define IONIZATION_CS_H class IonizationCS { public: IonizationCS(double ionization_potential); // constructor ~IonizationCS(); // destructor double get_cs_max(double T); private: static double A1, A2, B1, B2, a0, N, Ry; double S, F1, F2, I; }; // constructor inline IonizationCS::IonizationCS(double ionization_potential) { I = ionization_potential; // initializing static class members A1 = 0.94; A2 = 1.13; B1 = 2.3; B2 = 22.0; a0 = 5.3e-9; // Bohrradius [a0] = cm Ry = 13.6; // Rydberg energy [Ry] = eV N = 2; // number of electrons per shell S = 4 * M_PI * pow(a0,2)*pow(Ry/I,2); } // destructor inline IonizationCS::~IonizationCS(){} // get the maximal ionization cross section, simply depending on the energy of the primary electron, // thus the energy of the secondary electron is W = T - I // [sigma] = cm^2 inline double IonizationCS::get_cs_max(double T) { const double t = T/I; F1 = A1 * log(t)/(t + B1); F2 = A2 / (t + B2); return // S * F1 * (t-1)/(2*pow(t,2)) * (t + 1 - (4 * sqrt(t))/(3 + t)) // + S * F2 * (1 - 1/t - log(t)/(t+1)); } #endifEDIT:
Nein, ich muss noch das static herausnehmen, sonst knall es wieder!!Jetz läuft es aber!

Gruß,
Klaus.
-
Ja, so zum Beispiel. Wobei du es dann auch gleich mit in die Klassendefinition schreiben kannst. Und wobei diese Funktionen schon ziemlich groß und komplex sind.
Was mir aber gerade auffällt ist, dass da ganz was abgefahrenes mit deinen statischen Membern abgeht. Du reinitialisierst die, wenn ein neues Objekt erzeugt wird? Das ist ganz schön merkwürdiges Verhalten. Und geändert werden sie danach auch nie. Wieso sind das dann keine Konstanten?
Und alle Member, egal ob static oder nicht, außer I und S, werden nur in einer Funktion benutzt. Warum sind I und S nicht die einzigen Member (die dann übrigens auch besser in einer Initialisierungsliste initialisiert würden) und der Rest funktionslokal in get_cs_max?
Du sorgst dich also anscheinend um kleine Effizienzgewinne(inline), dafür machst du bei der grundlegenden Modellierung solche dicken Hauer (die dich am Ende dann auch Effizienz kosten werden). Da trifft mal wieder der berühmte Spruch zu, dass frühzeitigen Optimierung die Wurzel allen Übels ist. 
edit: Ach, jetzt hast du deinen Beitrag geändert, während ich geantwortet habe. Im wesentlichen passt meine Antwort aber noch.
statische Member müssen übrigens irgendwo leben (also irgendwo definiert werden), sonst knallt's beim Linken. Das dürftest du hier beobachtet haben.
edit2: Und der leere Destruktor ist auch typischer Cargo Cult.
-
Puh,
du bringst mich ja wieder ganz schön zum Grübeln - aber so soll es schließlich sein, sonst hätte ich mich hier nicht angemeldet!
SeppJ schrieb:
Was mir aber gerade auffällt ist, dass da ganz was abgefahrenes mit deinen statischen Membern abgeht. Du reinitialisierst die, wenn ein neues Objekt erzeugt wird? Das ist ganz schön merkwürdiges Verhalten. Und geändert werden sie danach auch nie. Wieso sind das dann keine Konstanten?
Also das Problem mit const ist ja, dass es nicht getrennt deklarieren und später initialisieren kann.
Das ganze lässt sich allerdings bei einer Klasse im Konstruktor als Ausnahme realisieren, wenn ich das richtig verstanden habe laut Wikieintrag.
Und dazu habe ich jetzt mal folgendes gebastelt:class IonizationCS { public: IonizationCS(double ionization_potential); // constructor double get_cs_max(double T); private: const double A1, A2, B1, B2, a0, Ry, N; double S, F1, F2, I; }; // constructor inline IonizationCS::IonizationCS(double ionization_potential): A1(0.94), A2(1.13), B1(2.3), B2(22.0),// a0(5.3e-9), // Bohrradius [a0] = cm Ry(13.6), // Rydberg energy [Ry] = eV N(2) // number of electrons per shell { I = ionization_potential; S = 4 * M_PI * N * pow(a0,2)*pow(Ry/I,2); }SeppJ schrieb:
Und alle Member, egal ob static oder nicht, außer I und S, werden nur in einer Funktion benutzt. Warum sind I und S nicht die einzigen Member (die dann übrigens auch besser in einer Initialisierungsliste initialisiert würden) und der Rest funktionslokal in get_cs_max?
Das verstehe ich jetzt nicht. Ich habe also meine physikalischen Konstanten in eine Initialisierungliste gepackt, aber wieso I und S?
SeppJ schrieb:
Du sorgst dich also anscheinend um kleine Effizienzgewinne(inline), dafür machst du bei der grundlegenden Modellierung solche dicken Hauer (die dich am Ende dann auch Effizienz kosten werden). Da trifft mal wieder der berühmte Spruch zu, dass frühzeitigen Optimierung die Wurzel allen Übels ist. 
Accepted - I am working on that!!
SeppJ schrieb:
statische Member müssen übrigens irgendwo leben (also irgendwo definiert werden), sonst knallt's beim Linken. Das dürftest du hier beobachtet haben.
Ja, das habe ich hier gemerkt. Aber wo müssen sie leben, dass sie global zur Verfügung stehen?
Soweit ich das verstanden habe reden wir ein wenig über den Unterschied zwischen const und static. Das Erste habe ich ein wenig als Selbstschutz des Programmierers verstanden. Also wenn er Variablen mit einem konstanten Wert haben möchte, dann deklariert er das ganze als const und kriegt einen Fehler ausgegeben, falls der Wert zur Laufzeit geändert werden würde (was ja nicht sein soll!)
Das Zweite ist ein wenig anders motiviert. Nämlich für den Fall, dass von dem Objekt der Klasse Kopien angelegt werden sollen, so wird das für static Members vermieden.
Bei mir würden Kopien angelegt werden, da mit jeder Kopie der Konstruktor aufgerufen wird, der wiederum ...Auf der anderen Seite versuche ich auch ganz allgemein so viel wie möglich schon zu deklarieren, dass ich später nur noch initialisieren muss - also Werte zuweisen. Ich dachte dann spare ich die Hälfte an Zeitaufwand.
Allerdings beißt sich da gerade für const Werte die Katze in den Schwanz. Bzw. im Sinne der physik. Konstanten, die einfach allgemein zur Verfügung stehen sollen, reden wir ja eher über static.Mir dreht sich langsam der Kopf ...
SeppJ schrieb:
edit2: Und der leere Destruktor ist auch typischer Cargo Cult.
Wieder was gelernt.
Gruß,
Klaus.