Trennung Header und Hauptteil
-
LogInNameVergessen schrieb:
evtl lag sein problem einfach nur an einer erneuten vorwärtsdeklaration :
void Beispiel();Definition der Funktion <----??? kann nicht gehen { Sleep(500);Der Funktionskörper } eher so: void Beispiel() //Definition der Funktion { Sleep(500);Der Funktionskörper }
Ich denke, dass das eher ein Tippfehler ist. Er wird wohl schon gewusst haben, dass man nicht einfach Text im Programm stehen lassen kann. Was hat das überhaupt mit einer Vorwärtsdeklaration zu tun?
-
@LogInNameVergessen: Nein, das kommt daher, dass in AutoIt Semikola für Strichpunkte stehen, habe ich irgendwie durcheinandergebracht
.
@Nexus: Ich lese doch deine Posts.... Ich mache es auch gerne nochmal, und dann zitiere ich alles missverständliche per Edit.
Edit:
Nexus schrieb:
Die Header- und Implementationsdateien müssen auch nicht den gleichen Namen haben, es ist jedoch empfohlen. Relevant ist nur, dass eine CPP-Datei die zugehörige H- oder HPP-Datei einbindet, um die Definitionen zu liefern.
Andere Dateien können dann diesen Header wiederum einbinden, um die Schnittstelle zu nutzen.
Also wie jetzt(?), um die Definitionen zu bekommen muss ich die Headerdatei einbinden?
Badestrand schrieb:
Die Rolle, die dabei Header-Dateien spielen: Wenn du zwei .cpp-Dateien hast und in beiden die Funktion "foo" benutzen willst, müsstest du sie in beiden .cpp-Dateien erst deklarieren, damit der Compiler sie in beiden Dateien auch kennt. Header-Dateien gibt es nun, damit du die Deklaration nur ein mal schreiben musst, und zwar in die Headerdatei. Diese Headerdatei bindest du in jede .cpp-Datei ein, in der du die Funktion(en) brauchst, die in der Header-Datei deklariert sind. Einbinden heißt inkludieren und das wiederum bedeutet, dass der Compiler beim Treffen der #include-Zeile in der .cpp-Datei eben diese #include-Zeile durch den kompletten Inhalt der Headerdatei ersetzt. Diese geht er dann ja durch und kennt nun die Deklarationen.
Ja, ich soll die Deklerationen in die Header-Datei schmeißen, nur wohin mit den Definitionen?
Dravere schrieb:
Vielleicht könnte man es mit ein paar g++ Befehle erklären. Dein präsentierter Code, würde man so kompilieren. Im ersten Schritt diesen Befehl:
g++ -c Beispiel.cpp
-c
bedeutet nur kompilieren, keine ausführbare Datei zu erstellen. Dafür wird eine Beispiel.o erstellt. Eine sogenannte Objektdatei.Im zweiten Schritt setzen wir das nun zusammen:
g++ -o Beispiel.exe AnderesBeispiel.cpp Beispiel.o
-o
bedeutet das man nun eine ausführbare Datei erstellt und benennt: Beispiel.exe. Man kompiliert AnderesBeispiel.cpp und fügt die Beispiel Objektdatei dazu. Der Linker wird dann die Definition in den Objektdateien (man kann auch mehrere benennen) suchen gehen, wenn er sie nicht in AnderesBeispiel.cpp findet.Grüssli
OK, nach mehrmaligem Lesen hat sich hier grad ein Missverständnis meinerseits aufgelöst, nur wie bringe ich den Compiler/Linker dazu das selbe ohne den Zwischenschritt zu machen?
pumuckl schrieb:
Und du bist sicher dass du das mit dem Kompilieren verstanden hast?
Du willst eine Funktion in beiden Übersetzungseinheiten haben (dem Compiler ist egal, ob die Dateien nun xy.cpp oder xy.jkl heißen, der kennt nur Übersetzungseinheiten).
Um das zu erreichen musst du den Code entweder per Hand in beide ÜEs schreiben oder ihn in eine Datei schreiben die du in beide ÜEs mit #include einbindest. Nachdem der Präprozessor rübergerutscht ist steht dann in jeder ÜE der Code den du eingebunden hast.
Jetzt darfst du mal raten welche Dateien jeweils der Grundstock für die erste und zweite ÜE sind und welche Datei man wo einbinden sollte...Naja, jetzt bin ich mir doch nicht mehr so sicher.
Gut, du schreibst, dass ich raten soll welche Datei das sein könnte: Ich hätte jetzt gesagt: Die Headerdatei und diese in die CPP-Dateien einbinden. Was sich allerdings dem ersten Post von Nexus wiederspricht(erster Listenpunkt):Nexus schrieb:
Header: Schnittstelle für den Benutzer, soll inkludiert werden
- Deklarationen von Funktionen und Variablen
- Klassendefinitionen
- Inline-Funktionen
- Templates
- Namensräume, Typedefs, Makros
- evtl. Kommentare, die Funktionen beschreiben
Ich hoffe ihr versteht mich so besser, und habt mich noch nicht aufgegeben.
-
Hmm. Habe jetzt nicht alles mitverfolgt, aber mal ganz einfach:
irgendwas.h
#ifnedf IRGENDWAS_H //Headerguards #define IRGENDWAS_H class foo { public: void bar (); // Deklaration }; #endif
irgendwas.cpp
#include "irgendwas.h" // Klassendefinition includen #include <iostream> //damit wir cout benutzen können :yum: void foo::bar () //Definition der in der Klasse deklarierten Funktion { std::cout << "irgendwas"; }
main.cpp
#include "irgendwas.h" //Klassendefinition include, da wir diese Klasse benutzen wollen int main () { foo f; f.bar (); }
Was dich ev. verwirrt ist das ganze Deklaration/Definitions Zeugs.
Was das bei einer Funktion bedeutet hast du gesehen. Nun kann man das ganze auch für eine Klasse machen..irgendwas_anderes.h
//.. Headerguards.. class foo; // Deklaration von foo,weil die Implementierung nicht wichtig ist (hier), da es nur ein Zeiger ist.. class ptr { foo* p; public: void machwas (); // wieder das gleiche, wie oben };
irgendwas_anderes.cpp
#include "irgendwas_anderes.h" // Wir müssen wissen, wie die Schnittstelle aussieht, damit wir die Funktionen implemenieren können. #include "irgendwas.h" // Müssen wir nun auch wissen, damit wir den ptr verwenden können. void ptr::machwas () //dasselbe { p->bar (); }
Wenn du jetzt noch verwirrter bist, dann schlaf drüber und schau dir das nochmal in Ruhe an.
Wenn du glaubst es verstandne zu haben, googlest du mal nach pImpl - Idiom und versuchst das zu verstehen. Wenn du das dann wirklich verstehst, dann hast du das ganze Spiel verstanden.
Noch eins:
Der Name der Header und der cpp müssen nicht gleich sein, das ist nur so Konvention, weil alles andere keinen Sinn machen und verwirren würde. (eigl. logisch, dass die gleich heissen, nicht?)
Wichtig ist aber nur, dass die Header includes stimmen (worauf dich aber dein Compiler sonst freundlich aufmerksam macht. ;))
-
Fast2 schrieb:
Dravere schrieb:
Vielleicht könnte man es mit ein paar g++ Befehle erklären. Dein präsentierter Code, würde man so kompilieren. Im ersten Schritt diesen Befehl:
g++ -c Beispiel.cpp
-c
bedeutet nur kompilieren, keine ausführbare Datei zu erstellen. Dafür wird eine Beispiel.o erstellt. Eine sogenannte Objektdatei.Im zweiten Schritt setzen wir das nun zusammen:
g++ -o Beispiel.exe AnderesBeispiel.cpp Beispiel.o
-o
bedeutet das man nun eine ausführbare Datei erstellt und benennt: Beispiel.exe. Man kompiliert AnderesBeispiel.cpp und fügt die Beispiel Objektdatei dazu. Der Linker wird dann die Definition in den Objektdateien (man kann auch mehrere benennen) suchen gehen, wenn er sie nicht in AnderesBeispiel.cpp findet.Grüssli
OK, nach mehrmaligem Lesen hat sich hier grad ein Missverständnis meinerseits aufgelöst, nur wie bringe ich den Compiler/Linker dazu das selbe ohne den Zwischenschritt zu machen?
Das geht nicht, der Zwischenschritt ist IMMER nötig! Und jede IDE macht diesen Zwischenschritt auch, nur bekommt man es vielleicht nicht mit, da es automatisiert von statten geht. Dies geschiet über sogenannte Makefiles, welche genau beschreiben, wie die einzelnen Schritte auszusehen haben.
Wenn du nur mit einem Texteditor und zum Beispiel g++ zusammenarbeitest, musst du jeden einzelnen Zwischenschritt selber erledigen. Anders geht es gar nicht.
Grüssli
-
Badestrand schrieb:
Der Compiler schert sich nicht um Dateinamen.
Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".
-
@drakon: Ich verstehe nur nicht wie der Code von der irgendwas.cpp in die main.cpp kommt... du inkludierst ja nur die Headerdatei, in der aber kein Vermerk auf die irgendwas.cpp steht.
Edit: Oh, ich war schon beim Verfassen und habe dswegen nicht bemerkt, dass etwas neues geschrieben wurde.
@Davere: Ja, JETZT verstehe ich das auch, man muss die Datei nicht im Code angeben, sondern nur über eine Objekt-Datei für den Linker sichtbar machen!.
Das war jetzt aber eine schwere Geburt!
Und wir haben mal wieder (zum Teil) aneinander vorbeigeredet... :headbang:
PS: Ich habe Code::Blocks und das schreibt die Makefiles selbst.
-
Fast2 schrieb:
@drakon: Ich verstehe nur nicht wie der Code von der irgendwas.cpp in die main.cpp kommt... du inkludierst ja nur die Headerdatei, in der aber kein Vermerk auf die irgendwas.cpp steht.
Meinst du nicht, das wurde inzwischen genügend häufig in diesem Thread erklärt?
Les diesen Thread am besten noch einmal durch. Ansonsten findest du im Internet sicher genügend Informationen zu der Vorgehensweise von Präprozessor, Compiler und Linker. Im Übrigen hab ich auch nicht das Gefühl, es würde etwas ändern, wenn ich den Vorgang erneut erläutern würde.
-
Ach ja, vergessen:
Danke!
-
~john schrieb:
Badestrand schrieb:
Der Compiler schert sich nicht um Dateinamen.
Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".
Wenn man unbedingt will, sollte man das auch irgendwo in der IDE anpassen können. (VC geht das).
@Fast2:
Na also. Geht doch.
-
~john schrieb:
Badestrand schrieb:
Der Compiler schert sich nicht um Dateinamen.
Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".
Du könntest natürlich auch so klug sein und anstatt gcc g++ verwenden. Das ist also nicht ein Problem des Compilers, sondern des Frontends, also des Programmes gcc, welches aber nichts mit dem C++ Compiler zu tun hat.
Ich bin mir jetzt nicht mehr sicher und habe an dieser Maschine keinen gcc, aber ich dachte man könnte sogar explizit angeben, welchen Compiler man möchte, wenn man einfach nur den gcc verwendet.
@Fast2,
Wusste ich doch, dass die Compiler Befehle helfen würden. Gerngeschehen.Grüssli
-
Naja, die Compilerbefehle waren jetzt nicht allein das ausschlaggebende, eher der Hinweis, dass das automatisch passiert, da ich einfach nicht verstanden habe, wie jetzt der Code von DateiA nach DateiB kommt. Ich dachte da gibt es einen speziellen Befehl, oder Syntax, oder so.
Muss ich den Thread jetzt irgendwo, irgendwie auf gelöst stellen?
PS: Irgendwie ist das Konzept (bei großen Projekten) doch doof: Der Linker muss sich ja durch die gesamte Objekt-Datei wühlen, nur um die Definition der Funktion zu finden, was ich mir bei großen Dateien schon als Bremse vorstellen könnte. Oder schreibt der Compiler eine Art "Inhaltsverzeichnis" (so hötte ich das gemacht), in dem dann irgendwie die Zeilennummern drinstehen, oder so? (Nur aus Interesse)
-
Fast2 schrieb:
Naja, die Compilerbefehle waren jetzt nicht allein das ausschlaggebende, eher der Hinweis, dass das automatisch passiert, da ich einfach nicht verstanden habe, wie jetzt der Code von DateiA nach DateiB kommt. Ich dachte da gibt es einen speziellen Befehl, oder Syntax, oder so.
Naja, auf die Compilerbefehle kam deine Frage, wie man es sonst macht und dann hast du erfahren, dass es automatisch geht. Denke schon, dass dies ziemlich ausschlaggebend war. Aber ist ja auch egal
Fast2 schrieb:
Muss ich den Thread jetzt irgendwo, irgendwie auf gelöst stellen?
Nein, das machen wir hier nicht, weil es sowieso nicht gemacht werden würde, wenn es sowas hätte. Der Thread wird sich langsam in den Untiefen des Archivs verlieren.
*den Thread streichel* schon gut Threadilein, du wirst dort viele Freunde treffenFast2 schrieb:
PS: Irgendwie ist das Konzept (bei großen Projekten) doch doof: Der Linker muss sich ja durch die gesamte Objekt-Datei wühlen, nur um die Definition der Funktion zu finden, was ich mir bei großen Dateien schon als Bremse vorstellen könnte. Oder schreibt der Compiler eine Art "Inhaltsverzeichnis" (so hötte ich das gemacht), in dem dann irgendwie die Zeilennummern drinstehen, oder so? (Nur aus Interesse)
Also vielleicht noch ein paar zusätzliche Bemerkungen dazu, bzw. noch ein Beispiel:
Header.hppvoid first(); void second(); void third();
First.cpp
#include "Header.hpp" void first() { }
Second.cpp
#include "Header.hpp" void second() { third(); }
Third.cpp
#include "Header.hpp" void third() { first(); }
Main.cpp
#include "Header.hpp" int main() { second(); third(); return 0; }
g++ Befehle dazu:
g++ -c First.cpp g++ -c Second.cpp g++ -c Third.cpp g++ -o Test.exe First.o Second.o Third.o
Wie du nun unschwer erkennen kannst, muss man die Objektdateien erst ganz am Ende übergeben, wenn die ausführbare Datei erstellt wird. Die Objektdateien selber brauchen nichts über andere Objektdateien zu wissen. Der Linker setzt am Ende alles zusammen und erstellt die ausführbare Datei. Er ist quasi der letzte Schritt beim Erstellen eines Programmes.
Ich kenne mich jetzt nicht genau in dem Bereich aus, aber du kannst dir sicher sein, dass die Linker sehr optimiert sind. Funktionsnamen dienen zur eindeutigen Identifizierung von den Funktionen. Man kann grundsätzlich über Bäume einfach die entsprechenden Funktionen finden. Häufig werden glaub ich auch Hashwerte für die Funktionen erstellt, wodurch man noch etwas mehr an Geschwindigkeit rausholen kann. usw. usf.
Es hat aber definitiv seine Vorteile, wenn du erst am Ende linken musst. Du kannst so während der Entwicklung immer nur kleinere Bereich kompilieren und damit schauen, ob der C++ Code auch stimmt. Ohne jedesmal noch alle anderen Bereiche neu zu komplieren oder zu linken, welche der entsprechende Bereich benötigt.
Grüssli
-
Um mal den Thread aus den Untiefen und von seinen Thread-Freunden weg zu holen.
Eine Frage:
Wäre es im Bezug auf große Projekte nicht einfach und sinnvoll, eine eigene "Bibliothek" oder Funktionssammlung zu schreiben? Also ich habe einen Header der alle Deklarationen vornimmt. Und eine z.B. my_lib.cpp in der die Funktionen komplett drinnen stehen. In meiner main.cpp würde ich dann einfach die Funktionen aufrufen und hab dort ein sehr einfachen/kurzen Code.Das ist eher mein Problem, dass ich mir nicht ganz sicher wäre was ich alles in eine extra *.cpp packe und was ich meiner "Hauptdatei" drinnen lasse.
AlphaX2
-
AlphaX2 schrieb:
Um mal den Thread aus den Untiefen und von seinen Thread-Freunden weg zu holen.
Ganz schlechte Idee. Erstelle einen neuen Thread und verlinke den alten, wenn du dich auf ihn beziehen willst.
AlphaX2 schrieb:
Also ich habe einen Header der alle Deklarationen vornimmt. Und eine z.B. my_lib.cpp in der die Funktionen komplett drinnen stehen.
Das ist ja die Standardvorgehensweise, nur dass es oft mehrere .hpp- und .cpp-Dateien werden, je nach Funktionalität eben.