Wo header inkludieren?



  • Hi,

    kurze simple Frage zu Headern:

    Wenn ich eine Klasse Foo im Header Foo.h deklariere:

    // Foo.h
    class Foo {
    public:
       std::string str;
    
       // Methoden etc.
    };
    

    brauch ich ja den header <string>. Wo muss/sollte ich den am besten überall inkludieren? Nur in Foo.h oder in Foo.h UND Foo.cpp oder nur in Foo.cpp?



  • Nur in foo.cpp => Typ bei Deklaration der Klasse nicht bekannt
    foo.cpp bindet foo.hpp ein, daher in foo.hpp .



  • nur in foo.h, wenn foo.cpp foo.h inkludiert ^^



  • K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?



  • this->that schrieb:

    K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?

    auf solche schweinereie sollte man sich _nie_ einlassen. das sorgt für geniale fehler...

    zur eigentlichen frage: it depends. 😉
    es bringt quasi keinen nachteil es in beide einzufügen im vergleich zum einfügen nur im header, da die abarbeitung der include-guards sehr schnell geht und der header dann eh noch im cache liegt.
    man kann aber in den header auch nur forward-deklarationen für die klassen einführen und erst in der source-datei den eigentlich header einbinden. das kann deutlich vorteile bei der übersetzungsgeschwindigkeit bringen, wenn du zum einen viele header hast und zum anderen nicht in jeder datei, die foo.h einbindet, tatsächlich alles gebraucht wird, was eigentlich außer bei der foo.cpp immer der fall ist.

    aus gründen der übersichtlichkeit und geschwindigkeit neige ich zu letzterem verfahren.



  • ghorst schrieb:

    this->that schrieb:

    K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?

    auf solche schweinereie sollte man sich _nie_ einlassen. das sorgt für geniale fehler...

    wieso? includes sind doch eh nur textersetzungen, oder nicht? mit nem #ifndef am anfang und nem #endif am ende einer headerdatei sollten damit alle fehlerquellen ausgeschlossen sein.

    aber generell: wenn du etwas in foo.h includierst, brauchst du es in foo.cpp nicht nochmals einzubinden. wozu auch?



  • ghorst schrieb:

    this->that schrieb:

    K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?

    auf solche schweinereie sollte man sich _nie_ einlassen. das sorgt für geniale fehler...

    zur eigentlichen frage: it depends. 😉
    es bringt quasi keinen nachteil es in beide einzufügen im vergleich zum einfügen nur im header, da die abarbeitung der include-guards sehr schnell geht und der header dann eh noch im cache liegt.
    man kann aber in den header auch nur forward-deklarationen für die klassen einführen und erst in der source-datei den eigentlich header einbinden. das kann deutlich vorteile bei der übersetzungsgeschwindigkeit bringen, wenn du zum einen viele header hast und zum anderen nicht in jeder datei, die foo.h einbindet, tatsächlich alles gebraucht wird, was eigentlich außer bei der foo.cpp immer der fall ist.

    aus gründen der übersichtlichkeit und geschwindigkeit neige ich zu letzterem verfahren.

    Meinst du statt:

    #include <string>
    class Foo { 
    public: 
       std::string str; 
    
       // Methoden etc. 
    };
    

    das:

    class string;
    class Foo { 
    public: 
       std::string str; 
    
       // Methoden etc. 
    };
    

    ? (is bissi Pseudocode, aber vom Prinzip her).

    Falls ja: aber das geht ja nur, wenn ich in Foo.h nirgends str benutze (z.B. wenn ich ne Methode Inline implementiere würde).
    War da nicht noch auch irgendwas, dass Vorwärtsdeklarationen nur bei Pointern und Referenzen funktionieren?



  • TravisG schrieb:

    ghorst schrieb:

    this->that schrieb:

    K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?

    auf solche schweinereie sollte man sich _nie_ einlassen. das sorgt für geniale fehler...

    wieso? includes sind doch eh nur textersetzungen, oder nicht? mit nem #ifndef am anfang und nem #endif am ende einer headerdatei sollten damit alle fehlerquellen ausgeschlossen sein.

    das ist nicht das problem, so wie ich das verstehe, will er folgendes machen:
    foo.cpp:

    #include <string>
    #include "foo.h"
    

    und dann in foo.h darauf verzichten string einzubinden. das funktioniert natürlich völlig problemlos, nur versuch mal in einen quelltext, der das im großen stil gemacht hat, irgendwas wieder zu verwenden...
    man sollte schlicht darauf verzichten die header so zu schreiben, dass die reihenfolge des inkludierens eine rolle spielt. sich durch solche fehlermeldungen zu kämpfen ist einfach nur ätzend. besonders, wenn es dann nicht mehr so augenscheinlich ist, sondern erst ein paar includes weiter kracht, weil da irgendein header irgendetwas inkludiert hat, das der andere dann auch noch nutzt...



  • this->that schrieb:

    Falls ja: aber das geht ja nur, wenn ich in Foo.h nirgends str benutze (z.B. wenn ich ne Methode Inline implementiere würde).

    ja. das stellt aber normalerweise nicht wirklich ein problem da.

    War da nicht noch auch irgendwas, dass Vorwärtsdeklarationen nur bei Pointern und Referenzen funktionieren?[/quote]
    ja, das ist richtig. ist aber je nach design auch nicht wirklich ein problem. in deinem falle wäre es eines, das ist richtig. aber da eher public-member hat, passt das schon. geht natürlich am einfachsten wenn man ein pimpl nutzt und bei get/set eh nur referencen durch die gegend reicht.



  • ghorst schrieb:

    TravisG schrieb:

    ghorst schrieb:

    this->that schrieb:

    K.
    Aber wenn Foo.cpp <string> VOR "foo.h" inkludiert, dann müsste es doch theoretisch auch gehen?

    auf solche schweinereie sollte man sich _nie_ einlassen. das sorgt für geniale fehler...

    wieso? includes sind doch eh nur textersetzungen, oder nicht? mit nem #ifndef am anfang und nem #endif am ende einer headerdatei sollten damit alle fehlerquellen ausgeschlossen sein.

    das ist nicht das problem, so wie ich das verstehe, will er folgendes machen:
    foo.cpp:

    #include <string>
    #include "foo.h"
    

    achso, naja dass ist wirklich sehr fahrlässig. zuletzt hab ich sowas im sourcecode des hl2-sdks gesehen 🙂 (da waren dann bei den includes so sachen gestanden wie "this file [in richtig fetter schrift] MUST [/schrift] be included before any other files are included". 👍



  • Einfach ueberall nicht einbinden, wo dies keinen Compilerfehler verursacht. f'`8k

    Autocogito

    Gruß, TGGC (making great games since 1992)



  • gute idee...



  • this->that schrieb:

    Hi,

    kurze simple Frage zu Headern:

    Wenn ich eine Klasse Foo im Header Foo.h deklariere:

    // Foo.h
    class Foo {
    public:
       std::string str;
    
       // Methoden etc.
    };
    

    brauch ich ja den header <string>. Wo muss/sollte ich den am besten überall inkludieren? Nur in Foo.h oder in Foo.h UND Foo.cpp oder nur in Foo.cpp?

    Bei großen Projekten sollten die Header-Abhängigkeiten minimiert werden, das beschleunigt das kompilieren großer Projekte deutlich, solange Referenzen oder Zeiger verwendet werden.

    // Foo.h
    namespace std { class string; }
    
    class Foo {
    public:
       std::string & str;
    
       // Methoden etc.
    };
    
    // Foo.cpp
    #include "foo.h"
    #include "string.h"
    

    Verwendest Du die vollständigen Typen, also "std::string str", muss std::string beim lesen von Foo.h bekannt sein. Also sollte es auch in Foo.h inkludiert werden.



  • Xin schrieb:

    Bei großen Projekten sollten die Header-Abhängigkeiten minimiert werden, das beschleunigt das kompilieren großer Projekte deutlich, solange Referenzen oder Zeiger verwendet werden.

    // Foo.h
    namespace std { class string; }
    
    class Foo {
    public:
       std::string & str;
    
       // Methoden etc.
    };
    
    // Foo.cpp
    #include "foo.h"
    #include "string.h"
    

    Verwendest Du die vollständigen Typen, also "std::string str", muss std::string beim lesen von Foo.h bekannt sein. Also sollte es auch in Foo.h inkludiert werden.

    Das ist doch bitte nicht dein Ernst?!

    1. ist es absolut verboten für den User was in den std-Namespace zu packen
    2. std::string ist ein typedef auf std::basic_string<char>! (Und falls du auf die Idee kommst das nun zu prototypen. STL-Container dürfen beliebige Extra-Template Params haben, wenn diese mit Default Werten belegt sind)
    3. ist std::string& und std::string zu unterschiedlich, um das mal über einen Tisch zu kloppen...

    und mit precompiled Headern, ist das Problem ja eh wesentlich geringer...



  • Man inkludiert normalerweise in der Main-Datei:

    //main.cpp
    #include <string>
    #include "foo.h"
    
    int main()
    {
    // ...
    return 0;
    }
    
    //foo.h
    
    #ifndef FOO_H
    #define FOO_H
    
    class Foo
    {
    public:
    // irgendwas :)
    
    private:
    std::string xyz;
    };
    
    #endif
    

    Ansonsten in der Foo.cpp(falls vorhanden).

    Edit: hab die Markos vergessen^^



  • XP^ schrieb:

    Man inkludiert normalerweise in der Main-Datei:

    //main.cpp
    #include <string>
    #include "foo.h"
    
    int main()
    {
    // ...
    return 0;
    }
    

    Den Stil finde ich schrecklich. Ein Header sollte alles enthalten, was man braucht um ihn zu benutzen. Man kann ja nicht von anderen Programmierern erwarten, dass er die Interna des Headers kennt (und das sollte er auch gar nicht). Außerdem sind doppelte Includes kein Problem, da die meisten Preprocs daraufhin eh optimiert sind.



  • rüdiger schrieb:

    Xin schrieb:

    Bei großen Projekten sollten die Header-Abhängigkeiten minimiert werden, das beschleunigt das kompilieren großer Projekte deutlich, solange Referenzen oder Zeiger verwendet werden.

    Verwendest Du die vollständigen Typen, also "std::string str", muss std::string beim lesen von Foo.h bekannt sein. Also sollte es auch in Foo.h inkludiert werden.

    Das ist doch bitte nicht dein Ernst?!

    1. ist es absolut verboten für den User was in den std-Namespace zu packen

    Ich packe nichts rein. Ich behaupte nur, dass es std::string gibt.

    rüdiger schrieb:

    2. std::string ist ein typedef auf std::basic_string<char>!

    Das wiederum ist ein Argument.
    Das war auch auf eigene Klassen gemünzt. Statt das angefragte Beispiel weiterzunutzen, hätte ich hier besser eine irgendeine Beispiel-Klasse genommen.

    rüdiger schrieb:

    3. ist std::string& und std::string zu unterschiedlich, um das mal über einen Tisch zu kloppen...

    Drum hab' ich Referenzen und eingebaute Objekte ja auch unterschieden. ^^



  • rüdiger schrieb:

    Den Stil finde ich schrecklich. Ein Header sollte alles enthalten, was man braucht um ihn zu benutzen.

    Hab ja nur ein Beispiel gezeigt 😉

    rüdiger schrieb:

    Man kann ja nicht von anderen Programmierern erwarten, dass er die Interna des Headers kennt (und das sollte er auch gar nicht).

    Sicher, das kann man :p

    rüdiger schrieb:

    Außerdem sind doppelte Includes kein Problem, da die meisten Preprocs daraufhin eh optimiert sind.

    Trodzdem ist das ein schlechter Stil(obwohl doppel besser hält), zumindest für mich.

    Mit freundlichen Grüßen,
    XP^



  • Geht das nicht mir forward-deklarationen?

    // Foo.h 
    class std::string;
    
    class Foo { 
    public: 
       std::string str; 
    
       // Methoden etc. 
        void foo(std::string bla);
    };
    

    Wozu muss den der Compiler hier wissen wie eine std::string aufgebaut ist? Solange man doch einfach behauptet, dass es einen std::string Typ gibt, sollte es doch reichen?

    Inline-Methoden sind doch eh unnuetz, genau wie das inline Schluesselwort? Das ist doch eh nur ein Vorschlag fuer den Compiler, dass diese Methode geinlined (sry. fuer das Wort) wird?



  • Xin schrieb:

    rüdiger schrieb:

    1. ist es absolut verboten für den User was in den std-Namespace zu packen

    Ich packe nichts rein. Ich behaupte nur, dass es std::string gibt.

    Und das darfst du nicht, selbst wenn es std::string in der Form gäbe.

    Xin schrieb:

    rüdiger schrieb:

    2. std::string ist ein typedef auf std::basic_string<char>!

    Das wiederum ist ein Argument.
    Das war auch auf eigene Klassen gemünzt. Statt das angefragte Beispiel weiterzunutzen, hätte ich hier besser eine irgendeine Beispiel-Klasse genommen.

    Wohl wahr, aber das Modell ist eben nicht auf die Standard-Lib oder andere Libraries 3. übertragbar! Ich finde es auch unverständlich, warum es im Standard auch nur für die Iostream-Sachen ein Forward-Header gibt. Aber im Grunde ist das mit den Headern ja nicht so dramatisch bei modernen Compilern.

    XP^ schrieb:

    rüdiger schrieb:

    Man kann ja nicht von anderen Programmierern erwarten, dass er die Interna des Headers kennt (und das sollte er auch gar nicht).

    Sicher, das kann man :p

    Ne, das ist schlechter Stil. Ich hoffe mal, dass du deine Klassen-Interfaces nicht ähnlich gestaltest.

    XP^ schrieb:

    rüdiger schrieb:

    Außerdem sind doppelte Includes kein Problem, da die meisten Preprocs daraufhin eh optimiert sind.

    Trodzdem ist das ein schlechter Stil(obwohl doppel besser hält), zumindest für mich.

    Weil?

    DEvent schrieb:

    Geht das nicht mir forward-deklarationen?

    Lies doch mal was Xin vorgeschlagen hat und vor allem meine Antwort dazu.

    DEvent schrieb:

    Wozu muss den der Compiler hier wissen wie eine std::string aufgebaut ist? Solange man doch einfach behauptet, dass es einen std::string Typ gibt, sollte es doch reichen?

    Ne, er muss ja zumindest die Größe eines std::string-Objektes kennen.

    DEvent schrieb:

    Inline-Methoden sind doch eh unnuetz,

    Ne

    DEvent schrieb:

    genau wie das inline Schluesselwort?

    Ne

    DEvent schrieb:

    Das ist doch eh nur ein Vorschlag fuer den Compiler, dass diese Methode geinlined (sry. fuer das Wort) wird?

    Und wenn es gar nicht erst inline ist, kann er die nur schwer inlinen. Außerdem ist es wichtig, um das Linkage für die Methode/Funktion richtig hinzubekommen. Ob der Compiler es nun für sinnvoll hält die Methode zu inlinen oder nicht, ist ne andere Sache.



  • rüdiger schrieb:

    Ne, er muss ja zumindest die Größe eines std::string-Objektes kennen.

    Ja stimmt, das habe ich vergessen.

    rüdiger schrieb:

    Und wenn es gar nicht erst inline ist, kann er die nur schwer inlinen. Außerdem ist es wichtig, um das Linkage für die Methode/Funktion richtig hinzubekommen. Ob der Compiler es nun für sinnvoll hält die Methode zu inlinen oder nicht, ist ne andere Sache.

    Das verstehe ich nicht ganz. Der Compiler entscheidet, ob die Methode inline wird oder nicht. Also wozu dann das inline Schluesselwort?

    Aus http://www.parashift.com/c++-faq-lite/inline-functions.html:

    There are several ways to designate that a function is inline, some of which involve the inline keyword, others do not. No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: it might inline-expand some, all, or none of the calls to an inline function.

    [9.3] Do inline functions improve performance?
    Yes and no. Sometimes. Maybe.

    There are no simple answers. inline functions might make the code faster, they might make it slower. They might make the executable larger, they might make it smaller. They might cause thrashing, they might prevent thrashing. And they might be, and often are, totally irrelevant to speed.

    Das sind mir irgendwie zu viele "vielleicht", "vielleicht nicht". Ist es da nicht das beste das inline Keyword ueberhaupt nicht zu benuzten und dem Compiler somit freie Hand lassen?


Anmelden zum Antworten