Header Dateien einbinden (#pragma once)
-
Hallo !
Irgendwie finde ich es "komisch" in einem Projekt mit z.B. 15 Cpp Dateien immer wieder eine bestimmte Header Datei einzubinden und deren Klasse/Variable zu nutzen.
Derzeit habe ich es gelöst: Eine "globale" Header/Cpp Datei, benötigte Header Dateien eingebunden, Klassen/Variablen global mit "extern" deklariert und fertig.
Störend ist immer wenn ich in eine der Header ändere muss immer das gesamte Projekt neu kompiliert werden und das dauert.Meine Frage ist jetzt, ob es alles dann doppelt und dreifach eingebunden wird wie z.B. Konstanten oder Variablen ? Wird dann auch jedesmal wieder neu Speicher benötigt. Habe allerdings in jeder Header Datei ein "#pragma once" stehen.
Gibt es irgendeine elegantere Möglichkeit ?
P.S.: Was sind eigentlich Zirkelreferenzen ?
-
Hier gibt es sicher schon etliche Beiträge zum Thema Include-"Wächter". Dort findest du bestimmt auch Beispiele.
mfg JJ
-
Hajo du findest es vielleicht komisch, weil du den Grundgedanken der Übersetzungseinheiten nicht kennst.
Gedacht ist es so, dass man an jeweils eine *.CPP Datei die benötigten Header oben einfügt, damit diese Datei dann eine Übersetzungseinheit darstellt.
Erst durch den Linker werden alle Compilierten Übersetzungseinheiten zusammengefügt.
Du solltest versuchen, deine Header nicht sooft zu ändern, denn sie dienen ja gerade dazu, Schnittstellen zu definieren, und Schnittstellen sollten sich immer am seltensten ändern.
Eine Faustregel ist, dass in eine Headerdatei nur Bekanntmachungen gehören, aber keine Sachen, die Speicher reservieren (Definitionen).
Eben weil Header dazu gedacht sind, mehrmals in einem Projekt eingebunden zu werden.Diese Include-Guards sind dazu da, Zirkel-Referenzen zu vermeiden falls,
z.B. ein Header eine Datei eins.h einbindet und in der Datei steht "#include "zwei.h" und dann in der zwei.h steht "#include "eins.h" ...
-
aufgrund der philosophie von c++ kommt man um das bedingte includieren von headerdateien nicht herum, da jede cpp-datei getrennt kompiliert wird und deshalb alle nötigen informationen erhalten muss. es gibt keinen mastercompiler der alles überwacht, und alle klassen kennt, sondern jede c++-datei wird einzeln ohne kenntniss der anderen c++-dateien kompiliert. daher muss man die klassen-definitionen in headerdateien packen, damit man sie in allen cpp-dateien, die sie benutzen, einbinden kann, denn sonst wüssten ja die entsprechenden dateien nicht, daß es die klasse gibt und welche eigenschaften sie hat (ausser die cpp-datei in der sie definiert ist, wenn man keine header benutzen würde).
die elegenteste art bedingt zu inkludieren ist meiner meinung nach mit #pragma once. dieses pragma once bewirkt, das in jeder cpp-datei eine header-datei nur einmal eingebunden wird, egal ob noch weitere header kommen, die den ersten header nochmals includieren. denn wenn in einer solchen headerdatei eine klasse definiert wird, dann wird sie ja unter umständen mehrmals definiert, und das darf nicht sein, daher -> #pragma once.
deine globale lösung ist nicht zu empfehlen. da man dann tatsächlich bei einer änderung alles neu kompilieren muss. der beste weg ist vorwärtsdeklarationen zu benutzen wann immer es geht und nur dann zu inkludieren wenn es nötig ist, dann verhindert man automatisch auch zirkelreferenzen.
//erst alles lesen. dann verstehen. :-D //in der header-datei immer die klasse definieren #pragma once #include "CString.h" //Inkludierung, da die klasse tatsächlich benutzt wird. class CVector; //Vorwärtsdeklaration, da die klasse nur als zeiger benutzt wird class CClass { public: CClass(void); ~CClass(void); public void SetX(CVector *vector); //hier reicht eine vorwärtsdeklaration, da der //kompiler nicht wissen muss, wie die klasse aufgebaut ist, sondern nur wissen muss, das es den bezeichner "CVector" gibt. aber in der datei die diese //funktion definiert, muss man dann wieder inkludieren, da diese datei ja die //klasse kennen muss, wegen speicherverbrauch der klasse private: CString string; //hier muss der kompiler wissen wie die klasse aufgebaut //ist, also wieviel speicher sie verbraucht. deshalb muss sie inkludiert werden };
prinzipiell kann man sagen, dass man immer dann, wenn man eine klasse nur als bezeichnr benutzt, zb mit zeigern auf die klasse oder zeigerzeigern(...) auf die klasse, man nur eine vorwärtsdeklaration braucht, und immer dann, wenn man tatsächlich eine instanz einer klasse verwendet, ein include braucht!
mit diesen regeln geht das kompilieren, vorallem beim benutzen von selbstgeschriebenen klassen sehr schnell, solange sie keine externen datentypen verwenden, da diese meistens lange headerincludes brauchen (zb. "windows.h" bei "HANDLE").
-
KXII schrieb:
@HaJo.
die elegenteste art bedingt zu inkludieren ist meiner meinung nach mit #pragma once.Ok, das nutze ich bzw. steht in JEDER Headerdatei.
KXII schrieb:
deine globale lösung ist nicht zu empfehlen. da man dann tatsächlich bei einer änderung alles neu kompilieren muss. der beste weg ist vorwärtsdeklarationen zu benutzen wann immer es geht und nur dann zu inkludieren wenn es nötig ist, dann verhindert man automatisch auch zirkelreferenzen.
//erst alles lesen. dann verstehen. :-D
Also, verstanden habe ich das noch nicht. Zur Zeit nutze ich noch die globale Methode. eine Header Datei globaldefs.h
#pragma once #include "Klasse_1_Header.h" #include "Klasse_2_Header.h" // hier Header von MyStruct inkludiert extern CMyClass1 m_Class1; extern CMyClass2 m_Class2; extern MyStruct m_Struct; // In der globaldefs.cpp // Variablen oder dergleich dann initialisieren
Diese Header Datei binde ich dann in alles Dialogfeldklassen ein, wo sie benötigt werden. (Es ist wichtig, das es sich immer um ein und die selbe Klasse/Struktur handelt, da diese während des ganzen Programmablaufs Daten zu verfügung stellt, die überall gelich sein müssen).
Mit der Vorwärtsdeklaration habe ich das jetzt nicht verstanden.