Wo header inkludieren?



  • 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?



  • rüdiger schrieb:

    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.

    Wer sagt das?

    Entweder stimmt die Deklaration, dann spare ich Zeit beim kompilieren.
    Oder es stimmt nicht, dann kann ich nicht kompilieren.

    rüdiger schrieb:

    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 war, aber das Modell ist eben nicht auf die Standard-Lib oder andere Libraries 3. übertragbar!

    Ich kann mich nicht erinnern, irgendwas im Bereich std deklariert zu haben, aber ich sehe da jetzt keinen Grund für, es nicht zu tun und Du lieferst mir keinen, außer "Du darfst das nicht".

    Gesetze ohne Begründung interessieren mich nur, wenn bei Zuwiderhandlung nennenswerte Strafen stehen. Andernfalls interessiert mich die Lösung des Problems mehr. 😉

    DEvent schrieb:

    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?

    Inline heißt, dass Du eine Funktion im Header verfügbar machen musst (und darfst) und dass der Compiler den Ratschlag erhält, die Funktion nicht zu rufen, sondern an den Aufrufen einzukompilieren.

    DEvent schrieb:

    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?

    Benutze inline dann, wenn Du sicher bist, dass die Funktion nicht zu einer Library gehört und sich ändern könnte. Alte Programme würden dann nicht die Funktion rufen, sondern die veraltete Version einkompiliert haben.
    Komplexe und aufwendige Funktionen sind mit großer Wahrscheinlichkeit nicht für inline gedacht. Hier kann der Compiler sich auch gegen den Ratschlag wenden und die Inline-Funktion als normale Funktion umsetzen.

    Für einfache Getter und Setter hilft inline, die Aufrufe (dadurch, dass sie nicht stattfinden) zu beschleunigen.



  • DEvent schrieb:

    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?

    Das inline Schlüsselwort dient vor allem dem Linker

    // foo.h
    void dosth() { /* ... */ }
    
    // foo.cpp
    #include "foo.h"
    
    // main.cpp
    #include "foo.h"
    
    int main() { }
    

    wird sonst ein Linkerfehler schmeißen, da dosth in zwei Übersetzungseinheiten vorhanden ist. Mit inline legt der Linker in jedem Objekt eine Kopie an oder ist zumindest in der Lage den Konflikt aufzulösen.

    Ist es da nicht das beste das inline Keyword ueberhaupt nicht zu benuzten und dem Compiler somit freie Hand lassen?

    Wenn der Compiler die Implementierung der Methode nicht sieht, wird er sie auch nicht inlinen können. Mittlerweile sind die Linker zwar schon etwas intelligenter geworden. Aber darauf kann man sich nicht verlassen.

    Xin schrieb:

    rüdiger schrieb:

    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.

    Wer sagt das?

    Der C++ Standard...

    Xin schrieb:

    rüdiger schrieb:

    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 war, aber das Modell ist eben nicht auf die Standard-Lib oder andere Libraries 3. übertragbar!

    Ich kann mich nicht erinnern, irgendwas im Bereich std deklariert zu haben, aber ich sehe da jetzt keinen Grund für, es nicht zu tun und Du lieferst mir keinen, außer "Du darfst das nicht".

    Gesetze ohne Begründung interessieren mich nur, wenn bei Zuwiderhandlung nennenswerte Strafen stehen. Andernfalls interessiert mich die Lösung des Problems mehr. 😉

    Schau halt in den Standard 🙄 Wie gesagt, es gibt STL-Implementierungen, die zB mehr Template-Parameter haben (Dinkumware macht das AFAIK). Was absolut konform ist! Also wirst du dich bei so etwas aus Ärger einstellen müssen.

    Und precompiled Header und "intelligentere" Precompiler haben das Problem ja ohnehin reduziert.



  • rüdiger schrieb:

    Das inline Schlüsselwort dient vor allem dem Linker

    Tolle Sprache, die dem Linker dient. Die Sprache soll mir dienen. 👎 Ich hoffe sowas wird im C++0x ausgebessert.


Anmelden zum Antworten