Hallo
Ich such ein gutes Textbuch über Programmierung in C++, das einem
erklärt, wie man selbstgeschriebene Headerfiles organisiert, also was
kommt in das .h , was kommt in das .cpp , in welcher Reihenfolge weden
die #include Anweisungen in die einzelnen Dateien gesetzt und wie
verdaut der Compiler bzw. Linker das ganze.
Tips, wie man es macht hab ich schon ein paar gelesen, aber ich würde
das Ganze gerne insgesamt verstehen.
Bücher in englisch sind mir genauso recht, da ich englische Originale
den übersetzten Versionen vorziehe.
Vielen Dank dafür
Wahrscheinlich findest du dazu mehr in jedem C-Buch.
Der Unterschied ist minimal.
In C immer und in C++ weitestgehend ist es so, daß alle Deklarationen
in die .h kommen und die Definitionen in die .c - und zwar
gleichermaßen für Funktionen wie für globale Variablen.
Typdefinitionen und Makros landen in der .h, wenn sie in mehreren
Quelltexten genutzt werden.
In C++ kommt inline dazu, sei es explizit oder bei innnerhalb
von class{ ... } definierten Methoden implizit.
Dann stehen auch diese Methoden (also ggf. mit der gesamten
class{ ... }) in der .h.
Vielleicht reicht dir das schon:
http://www.wachtler.de/ck/16_Aufteilung_mehrere_Quell.html
Im Grunde ist es sehr einfach:
Du teilst dein Projekt in Module ein. Ein Modul ist eine Einheit, die
einen bestimmten Themenkreis bearbeitet. Wie zb Stringverwaltung oder
Kundendaten oder was auch immer.
Dann überlegst du, welche Teile deines Moduls den Verwender des Moduls
interessieren, welche Datentypen muss der kennen wenn das Modul
beispielsweise irgendwelche Strukturen oder Klassen definiert, welche
Funktionen muss ein Verwender benutzen können, welche globalen Variablen
muss ein Verwender sichtbar und zugreifbar haben.
All das kommt ins Header File.
Alles andere kommt ins cpp-File (oder C File, das Prinzip ist dasselbe).
Dazu überlegt man noch, welche weiteren Includes jeweils nötig sind,
damit das Header File bzw cpp-File in sich vollständig ist. Wird in
einem Header File ein Datentyp 'hamstibamsti' benutzt, der nicht Teil
dieses Moduls ist, dann setzt man einen entsprechenden Include an den
Anfang. Einfach nach der Regel vorgehen: Der Compiler arbeitet den Code
von oben nach unten ab und verwendet werden kann nur das was bereits
bekannt ist. Ein Datentyp "hamstibamsti" muss also deklariert werden ehe
er das erstmal zb in einer Klasse benutzt werden kann. Daher muss der
entsprechende #include vor der verwendenden Klassendeklaration kommen.
Im Grossen und Ganzen wars das auch schon.
Der eigentliche Knackpunkt am Ganzen liegt ganz woanders: Wie komme ich
zu einer sinnvollen Aufteilung in Module?
Manchmal ist das ganz einfach und naheliegend. Manchmal hat man mehrere
Möglichkeiten, die sich anbieten und man muss eine auswählen und
manchmal bietet sich zunächst scheinbar überhaupt nichts an.
Ach muss man des öfteren eine künstliche Grenze einziehen. Gesucht ist
die goldene Mitte zwischen "Die Modularisierung ist viel zu grob und
bringt so gut wie gar nichts" und "Die Modularisierung ist viel zu fein
und verschleiert und verkompliziert nur alles"
Da liegt der Knackpunkt und das kann dir auch kein Buch beibringen. Am
Anfang liegt man des öfteren schon mal daneben. Dann darf man aber auch
keine Scheu haben, die Modularisierung noch mal zu überdenken und zu
überarbeiten und zu sehen, ob man mit einer anderen Aufteilung die
festgestellten Probleme besser in den Griff kriegt. Nichts ist so
tödlich, wie die Neigung von Einsteigern, die Programmteile die sie
schon haben als "von Gott gegeben und daher unveränderbar" anzusehen.
Genausowenig wie dir ein Buch beibringen kann, ein berühmter Maler oder
Komponist zu werden, genausowenig kann dir ein Buch die Erfahrung geben,
wie man Modularisierung machen kann. Es gibt Daumenregeln, aber es gibt
kein Kochrezept, dem man einfach nur folgen muss und dann ist alles gut.
Also... Das was Klaus schreibt hab ich soweit verstanden. Die einzelnen
Bereiche des Programmes kommen in verschiedene .c Dateien rein, das was
für alle gemeinsam gebraucht wird kommt in eine .h, die dann in all die
Module includiert wird.
Nur, wie mache ich das dann, daß mir all die verschiedenen .c dateien
jetzt zu einem Programm zusammenkompiliert werden?
Ich sollte vielleicht dazu sagen, daß ich mit Eclipse und GCC unter
Linux arbeite...
Danke schon mal für die ausführlichen Antworten
hubert schrieb:> Also... Das was Klaus schreibt hab ich soweit verstanden. Die einzelnen> Bereiche des Programmes kommen in verschiedene .c Dateien rein, das was> für alle gemeinsam gebraucht wird kommt in eine .h, die dann in all die> Module includiert wird.
Jein.
Bei kleineren/mittleren Projekten kann man es so machen.
Es ist aber oft ein Zeichen von Planlosigkeit...
Meist läuft es auf folgende Regeln hinaus:
- Das gesamte Programm besteht aus verschiedenen Modulen, die
entweder unabhängig voneinander bestehen, oder teilweise
aufeinander aufbauen.
Ein Modul könnte z.B. sein:
* alles was mit Datenbanken zu tun hat,
* alle Hilfsfunktionen zur Matrizenrechnung,
* alles für Finite Elemente, ...
Man kann sein Programm also senkrecht unterteilen (Datenbanken
haben nicht mit linearer Algebra zu tun, die beiden kennen sich
also nicht) oder waagrecht in verschiedene Schichten (Finite
Elemente benötigen ein Modul zur linearen Algebra, wird aber
wiederum vom Hauptprogramm benötigt).
Das Ganze kann man sich schön als Bild von Hierarchien malen
nach verschiedenen Regeln.
Die Aufteilung in Module ist natürlich bis zu einem gewissen
Grad willkürlich, aber je nach Problem gibt es sinnvolle oder
dämliche Varianten.
Zu jedem Modul gibt es eine .h, also z.B. eine linearealgebra.h,
eine fem.h, eine db,h und so weiter.
Da kommen jeweils Typdefinitonen rein, sowie Deklarationen von
globalen Daten und Funktionen.
Diese .h wird dann eingerahmt von:
1
// Kommentar blabla .. von wem warum weshalb Geschichte...
2
#ifndef _FEM_H_
3
#define _FEM_H_
4
#include<alles>
5
#include<was>
6
#include<modul>
7
#include<so braucht...>
8
...alleTypdefinitionen,alleDeklarationen...
9
#endif // _FEM_H_
- Falls es daneben noch Definitionen gibt, dann packt man diese
in eine .c gleichen Namens, also linearealgebra.c, fem.c, db.c.
Die .c #includet am besten auch ihre zugehörige .h.
Dadurch fallen Abweichungen zwischen Deklaration und Definition
schnell durch Compilerfehler auf.
Die .c und .h treten also meistens paarweise auf.
>> Nur, wie mache ich das dann, daß mir all die verschiedenen .c dateien> jetzt zu einem Programm zusammenkompiliert werden?>> Ich sollte vielleicht dazu sagen, daß ich mit Eclipse und GCC unter> Linux arbeite...
Ich nehme nicht Eclipse, aber da wird es irgendeine Art von
Projektverwaltung geben, wo man die .c einträgt für jedes Projekt.
PS: Bei C++ ist das alles gleich, nur halt *.cpp statt .c.
Außerdem wie bereits oben erwähnt kommen bei C++ alle
Definitionen von inline-Funktionen/Methoden auch indie .h, nicht
in die .cpp.
Das ist jetzt gut verständlich.
Zur Sicherheit: Hab ich das richtig verstanden? Wenn Definitionen zu den
Deklarationen dazukommen (Funktions- oder bei C++ Klassendefinitionen)
und ich schreib sie in eine extra .c oder .cpp dann includiere ich das
.c oder .cpp in den header rein. Muß ich dieses include dann am Ende der
Headerdatei reinschreiben, so daß sie nach den Definitionen kommt? Oder
ist das egal?
hubert schrieb:> Das ist jetzt gut verständlich.>> Zur Sicherheit: Hab ich das richtig verstanden? Wenn Definitionen zu den> Deklarationen dazukommen (Funktions- oder bei C++ Klassendefinitionen)> und ich schreib sie in eine extra .c oder .cpp
Soweit sie halt thematisch zusammenpassen, man muß nicht für jeden
Pillepallekram eine eigene Datei aufmachen.
> dann includiere ich das> .c oder .cpp in den header rein.
Nein!!
Was habe ich da angerichtet!
Eine .c oder .cpp wird (fast) nie irgendwo mit #include eingebunden!
Bei einer IDE werden die einzelnen .c und .cpp von der Projektverwaltung
zusammengeschraubt.
Unter der Haube passiert dann das gleiche wie bei manuellem Übersetzen
oder wie bei mir mit make:
Jede .c oder .cpp wird einzeln kompiliert vom Compiler, dabei werden
die jeweils mit #include angegebenen .h eingebunden.
Dabei entsteht aus jeder .c und jeder .cpp je eine Objektdatei
(unter Linux .o, unter Windows .OBJ).
Anschließend wird der Linker angeworfen. Der packt alle .o bzw. .OBJ
und die nötigen Bibliotheken und klebt das alles zu einem
ausführbaren Programm zusammen.
In deinen .c und .cpp stehen nur #includes für die Headerdateien
(.h, bzw. die System-Header bei C++ ohne .h dran, wie bei
#include <iostream>).
Wenn du eine .c oder .cpp mit #include einbindest, hast du
mit 99.9-prozentiger Sicherheit etwas nicht verstanden.
Du schreibst deine .h (siehe oben).
Du schreibst deine .c oder .cpp, darin stehen #include für die .h.
Dann sagst du deinem Eclipse oder wem auch immer, welche
.c kompiliert werden müssen (und damit implizit, welche
Objektdateien später gelinkt werden müssen).
Das wars.
Langsam wird das Bild noch klarer. So wie Du das jetzt beschreibst hab
ich das in der Ausführlichkeit noch nirgends gelesen. Anscheinend ist
das ein Thema, das unter ernsthaften Programmierern von Mund zu Ohr
weitergegeben wird und das kaum einem Lehrbuch mit seinen
<100-Zeilen-Beispielen wirklich drinsteht oder in den Grundlehrgängen
geschult wird. Scheint so was wie der Sprung von Lehrgangstheorie zur
harten Alltagspraxis zu sein.
hubert schrieb:> Langsam wird das Bild noch klarer. So wie Du das jetzt beschreibst hab> ich das in der Ausführlichkeit noch nirgends gelesen. Anscheinend ist> das ein Thema, das unter ernsthaften Programmierern von Mund zu Ohr> weitergegeben wird
wenn du gut aufgepasst hast, dann ist dir vielleicht aufgefallen, dass
der exakte Meschanismus, wie man seiner IDE mitteilt, welche Einzelteile
alle zum kompletten Projekt gehören, von der IDE selber abhängt.
Im Extremfall braucht man gar keine IDE
Das Projekt bestehe aus
* main.c dem Hauptprogramm, welches linear Algebra macht
* linalg.c da drinnen sind alle Funktionen, die sich um lineare
Algebra drehen
dann wirft man seine Command line an und
* compiliert main.c einzeln, zb
1
> cc main.c /OUTPUT=main.o
2
CC: compiling main.c .... done
3
0 errors, 0 warnings
4
>
* compiliert linalg.c einzeln, zb
1
> cc linalg.c /OUTPUT=linalg.o
2
CC: compiling linalg.c .... done
3
0 errors, 0 warnings
4
>
* linkt die beiden Einzelteile zum kompletten Programm zusammen, zb
1
> link main.o linalg.o /OUTPUT=main.exe
2
Linkage Editor V 5.8: linking
3
* main.o
4
* linalg.o
5
* default system library
6
-> program created in 'main.exe'
7
0 errors, 0 warnings
8
>
und erhält so das fertige Programm main.exe, welches man ausführen kann.
Mit einer IDE (Integrated Development Environment) ist das deutlich
einfacher: Man sagt der IDE: zu meinem Projekt gehören main.c und
linalg.c und die IDE kümmert sich darum, dass unter der Haube genau die
gleichen 3 Schritte ausgeführt werden, wie man sie selbst auch hätte
händisch machen können. Nur: Wie genau man der IDE mitteilt, was alles
zum Projekt dazugehört steht im Handbuch der IDE und nicht in einem
C-Lehrbuch.
Beim Kompilieren fehlt (zumindest wenn man den gcc nimmt) jeweils
noch die Option -c, damit nur kompiliert wird und nicht gleich gelinkt,
und die Option, die die Ausgabedatei benennt, ist -o statt /OUTPUT,
zudem wird der gcc auch gleich zum Linken verwendet und die
Ausgabedateien enden üblicherweise nicht auf .exe.
Sonst ist aber fast alles gleich :-)