Deklaration und Definition - Sinnvolle Verpackung gesucht



  • Hi,

    soweit ich weiß muss ich ja nicht immer die komplette Headerdatei mit allen Funktionen einer Klasse einbinden (Definition(?)), wenn ich diese benutzen möchte. Manchmal reicht ja auch nur die Deklaration(?) der Klasse (class test;).

    Wie teilt ihr diese Aufgaben in eurem Projekt auf?
    Packt ihr alle Deklarationen in eine Headerdatei und fügt diese in fast allen Projektdateien ein? Oder erstellt ihr für jede Klasse eine eigene Deklarationsheaderdatei? Oder gibt es sogar eine Möglichkeit das der Compiler beim #includen der Headerdatei entscheidet ob er die komplette Klasse mit allen Funktionen oder nur die Deklaration einfügen soll?

    Ich bin gespannt auf eure Vorschläge und Ideen.

    [Edit]
    Währe es eine "saubere" Lösung, wenn ich die Deklaration vor das #ifndef meiner Headerdatei packe?
    Sinn des ganzen ist unter anderem das ich nicht möchte das beinahe mein gesamtes Projekt neu kompiliert werden muss, wenn ich etwas an der Definition der Klasse ändere. Dann werden im Moment aber auch die Dateien neu kompiliert die eigentlich nur die Deklaration benötigen.
    [/Edit]

    MfG
    Scarabol



  • Scarabol schrieb:

    soweit ich weiß muss ich ja nicht immer die komplette Headerdatei mit allen Funktionen einer Klasse einbinden (Definition(?)), wenn ich diese benutzen möchte. Manchmal reicht ja auch nur die Deklaration(?) der Klasse (class test;).

    Ja

    Scarabol schrieb:

    Wie teilt ihr diese Aufgaben in eurem Projekt auf?

    Ich schreibe die Deklarationen einfach hin, wenn es keine komplizierten Templateklassen sind (ansonsten mach ich so etwas wie <iosfwd>

    Scarabol schrieb:

    Oder gibt es sogar eine Möglichkeit das der Compiler beim #includen der Headerdatei entscheidet ob er die komplette Klasse mit allen Funktionen oder nur die Deklaration einfügen soll?

    #include ist dumm! Das kann nichts entscheiden. Dein Compiler sieht es noch nicht einmal, da es der Prepro macht! #include ist einfach nur Textersetzung

    (zB mit dem GCC lässt sich das einfach zeigen.

    bar.h

    Hallo
    

    foo.cpp

    #include "bar.h"
    Welt
    

    (man beachte das weder bar.h noch foo.cpp so gültiges C++ sind)

    g++ -E foo.cpp
    # 1 "foo.cpp"
    # 1 "<eingebaut>"
    # 1 "<Kommandozeile>"
    # 1 "foo.cpp"
    # 1 "bar.h" 1
    hallo
    # 2 "foo.cpp" 2
    Welt
    

    (Die Zeilen mit # sind nur Hinweise für den Compiler, damit er es schafft bei Fehlermeldungen die Zeilen der richtigen Datei zuzuordnen)



  • Manchmal reicht ja auch nur die Deklaration(?) der Klasse (class test;).

    Nennt sich forward declaration. Das geht, wenn du nur Referenzen oder Zeiger dieser Klasse verwendest. Willst du Methoden aufrufen, dann brauchst du immer die ganze Klasse. Meist verwende ich in Headerdateien forward declaration, sofern es geht. In cpp-Dateien brauche ich dann aber meist die ganze Klasse mittels #include.

    Währe es eine "saubere" Lösung, wenn ich die Deklaration vor das #ifndef meiner Headerdatei packe?

    Include guards haben damit nichts zu tun. Nein es waere keine saubere Loesung.

    Dateien neu kompiliert die eigentlich nur die Deklaration benötigen

    Wenn dem so ist, dann schreibe einfach die forward declaration hin, ohne die entsprechende Datei zu includieren.



  • ok, danke.

    Schreibt ihr die Deklarationen immer in jeder Headerdatei neu oder packt ihr diese in eine gesammelte oder wie ists am besten?

    Eigentlich ist es ja egal. aber ich habe mittlerweile 64 .h und .cpp Dateien und da will ich nur sichergehen, dass wenn das Projekt weiterwächst. Es trotzdem noch einigermaßen leicht zu warten bleibt.

    MfG
    Scarabol



  • Schreibt ihr die Deklarationen immer in jeder Headerdatei neu

    Ja. Ich gebe explizit an, was ich benoetige.

    Es trotzdem noch einigermaßen leicht zu warten bleibt.

    Schreibfaul != wartbar



  • Hi,

    ich hab mittlerweile auch das Problem, dass ich die Klassen soweit ineinander verschachtelt habe, dass ich immer nur die Meldung "Verwendung des undefinierten Typs..." bekomme wenn ich die Reihenfolge der Includes ändere oder neue hinzufüge :-((

    Werd mir wohl mal eine Grafik oder so machen müssen, um das ganze zu sortieren...
    Oder gibt es dazu andere Methoden? Ich bin kein guter Zeichner und im Kopf schaff ich das auch nicht "mehr"

    MfG
    Scarabol



  • Das, was du da hast sind zyklische includes. Was das genau heisst und wie du das beheben kannst findest du hier.

    Aufzeichnen kannst du das eigentlich als gerichteten Graphen der Abhängigkeiten der Files und da siehst du dann relativ schnell, dass du einen Zyklus hast.

    Du wirst so etwas hier gebaut haben:
    http://de.wikipedia.org/w/index.php?title=Datei:Graph_gerichtet.svg&filetimestamp=20070901183550

    Wobei die Knoten (A,B usw.) die Files darstellen und ein Pfeil ein include. Und wenn du dir das mal vorstellst darf ein File sich niemals direkt oder indirekt includen.



  • knivil schrieb:

    Manchmal reicht ja auch nur die Deklaration(?) der Klasse (class test;).

    Nennt sich forward declaration. Das geht, wenn du nur Referenzen oder Zeiger dieser Klasse verwendest.

    Oder diese Klasse als Rückgabetypen oder Funktionsparameter benutzt. Wird von vielen gern vergessen.



  • Die Aufzeichnung der Includeabhängigkeiten lässt sich schön mit doxygen ganz automatisch machen. Wenn GraphViz mit dazu installiert ist werden diese Graphen mit erzeugt.



  • Scarabol schrieb:

    Hi,

    ich hab mittlerweile auch das Problem, dass ich die Klassen soweit ineinander verschachtelt habe, dass ich immer nur die Meldung "Verwendung des undefinierten Typs..." bekomme wenn ich die Reihenfolge der Includes ändere oder neue hinzufüge :-((

    Um sowas aufzuräumen gehe ich meist wie folgt vor:

    - nehmen wir an, ich hab eine X.cpp und die zugehörige X.hpp. Dann stelle ich in der X.cpp die X.hpp als erstes #include ein. Damit krieg ich als erstes Fehlermeldungen aus diesem Header (direkt oder indirekt aus den Headern, die X.hpp einbindet), danach aus den anderen Headern, die X.cpp einbindet, danach aus X.cpp selber.

    - ich kommentiere sämtliche #includes und forward-decls aus X.hpp aus, genauso alle #includes aus X.cpp, mit Ausnahme von X.hpp. Ich versuche X.cpp zu compilieren. Ich kriege zuerstmal Fehlermeldungen aus X.hpp über unbekannte Typen usw. Zu jedem Fehler entscheide ich, ob eine forward-decl reicht oder ob ein #include mit der entsprechenden Klassendefinition nötig ist.
    danach nächster compile-Versuch. Jetzt bekomme ich evtl. Fehlermeldungen aus den in X.hpp eingebundenen Headern. Falls das so ist gehe ich für jeden Header Y.hpp der einen Fehler erzeugt das Selbe Procedere mit seiner Y.cpp durch, quasi rekursiv. (Die Rekursion terminiert dank der Includequards)

    - ich hab jetz nurnoch Fehler in der X.cpp. Ich binde für jeden Fehler den nötigen Header wieder ein, ab und an reicht auch hier eine Forward-decl, z.B. wenn ich nur Pointer von A nach B reiche ohne auf das dahinterliegende Objekt zuzugriefen.

    - ich freu mich, dass ich die Hälfte der vorher auskommentierten #includes nicht wieder einkommentieren musste und deshalb einige Compile-Abhängigkeiten rauswerfen konnte. Das gleiche Spiel kann ich jetzt auch mit dem Linker machen, indem ich zuerst garkeine Bibliotheken mitlinke und nachschaue, was ich wirklich dazulinken muss. Das trifft aber eher nur dann zu wenn man an einer größeren Software arbeitet wo mehrere hundert .libs gegenseitige Abhängigkeiten haben.

    Das Ganze ist ziemlich umständlich und kostet Zeit. Man kanns etwas abkürzen, wenn man z.B. die unbedingt nötigen #includes schon vorher sieht (Basisklassen und Member, die keine Referenzen oder Pointer sind). Aber allgemein kann mir der Compiler selbst am besten sagen was er braucht so dass ich nicht voreilig alles #include was mir zu dem Thema in den Sinn kommt. So eine Aufräumaktion starte ich auch nur, wenn ich ein größeres Refactoring hinter mir habe oder neue Funktionalität eingebaut habe wo neue Klassen entstanden sind und/oder größere Codeteile umgezogen sind bzw. sich grundlegend geändert haben. Aber hin und wieder lohnts sich und wenn der Build der Komponente dadurch ein paar Sekunden schneller geht und die Zusammenhänge übersichtlicher werden rechnet sich der Aufwand.


Anmelden zum Antworten