Vorwärtsdeklaration



  • Rolff schrieb:

    Was ist der Vorteil einer Vorwärtsdeklaration zu einem include?

    Es werden weniger Abhängigkeiten erzeugt. Zyklische Abhängigkeiten werden vermieden.

    Gibt es eine Situation, in der es keinen Unterschied macht, wenn man eine Vorwärtsdeklaration oder ein include macht?

    Wenn man einen Header inkludiert, in dem nur die Vorwärtsdeklaration steht.

    Gibt es eine Situation, in der ein include besser ist als eine Vorwärtsdeklaration?

    Wenn eine Vorwärtsdeklaration nicht ausreichend ist.



  • Guten Morgen.

    Also der Trick ist folgender. Wenn du 2 Funktionen hast nennen wir sie A und B. Und nun rufst du in funktion A die Funktion B auf. Dann muss der Compiler zur Laufzeit wissen, was mit Funktion B gemeint ist. Nun kannst du natürlich B vor A Deklarieren . Also in Etwa so :

    void FunktionB()
    {
    }
    
    void FunktionA()
    {
    FunktionB();
    }
    

    Eine andere Möglichkeit ist es eine Vorwärtsdeklartion durchzuführe ;:

    void FunktionB();
    void FuntkionA();
    
    void FunktionB()
    {
    }
    
    void FunktionA()
    {
    FunktionB();
    }
    

    Nun weiß der Compiler berits über die Funktionen bescheid, aber noch nichts über das Innenleben, das kommt später.



  • Bei Funktionen find ich es jetzt nicht wichtig, eher vielleicht bei Klassen:

    class B;
    
    class A
    {
        B* b;
    };
    
    class B
    {
        A* a;
    };
    

    Aber es kommt selten vor, dass ich das brauche.



  • FreakY<3Cpp schrieb:

    Aber es kommt selten vor, dass ich das brauche.

    Wieso "brauche"? Eine Vorwärtsdeklaration ist das Minimum, wenn man einen Typ benutzen will. Wenn man damit auskommt, ist das gut. "Brauchen" tut man ein Include, und zwar holt man sich damit in der Regel mehr Abhängigkeiten (und Compilezeit) ins Boot, als man haben will.



  • FreakY<3Cpp schrieb:

    Aber es kommt selten vor, dass ich das brauche.

    Wirklich? Ich setze die Vorwärtsdeklarationen so oft wie möglich ein, weil sie Abhängigkeiten und somit Kompilierzeit einsparen. Ich hätte starke Bedenken, wenn ich das nicht tun würde (abgesehen davon, dass es z.B. bei zyklischen Abhängigkeiten gar nicht ohne geht).

    Ich mache teilweise sogar sowas:

    namespace AnyLib // fremde Bibliothek
    {
        template <typename T>
        class AnyClass; // dort vorkommendes Template
    
        typedef AnyClass<float> FloatClass; // dort vorkommendes Typedef
    }
    


  • Hmm. Das halte ich aber für gefährlich, da du dich um Sachen kümmern musst, die eigentlich nur die Bibliotheksentwickler kümmern müsste..
    Ich denke, dass es da auch Portabilitätsprobleme geben kann. (z.B wie in der STL)

    Ich hoffe daher, dass das eher eine Ausnahme bildet, wenn es wirklich nicht anderst geht. 😉


  • Administrator

    drakon schrieb:

    Hmm. Das halte ich aber für gefährlich, da du dich um Sachen kümmern musst, die eigentlich nur die Bibliotheksentwickler kümmern müsste..
    Ich denke, dass es da auch Portabilitätsprobleme geben kann. (z.B wie in der STL)

    Ich hoffe daher, dass das eher eine Ausnahme bildet, wenn es wirklich nicht anderst geht. 😉

    Was soll daran gefährlich sein? Worum müsste man sich da kümmern, was nur der Bibliotheksentwickler kümmern müsste? Ich muss ja sowieso wissen wo die Klasse ist und wie sie heisst.
    Ich mache dies absolut auch regelmässig. Eine Vorwärtsdeklaration sollte man immer einem include vorziehen, wenn es denn geht.

    Grüssli



  • Bei Standardsachen kann das in der Tat gefährlich sein, weil StdLib-Implementierungen noch zusätzliche Default-Templateparameter bereitstellen dürfen und man so keine Portabilität gewährleisten kann. Aber ich meinte jetzt abgesehen davon. Und da ist eine Vorwärtsdeklaration sicher besser, als die Headerdateien (welche bei Templates immer noch die gesamte Implementierung enthalten) einzubinden.



  • @dravere:
    Ich habe das auf Nexus bezogen und den typedef Vorwärtsdeklarationen.

    z.B Wie deklarierst du einen Iterator für basic_string vor?

    21.3/6 schrieb:

    typedef implementation defined iterator;

    Dazu kommt noch, dass es per Standard gar nicht erlaubt ist std zu erweitern. (also geht die obige Methode schon mal nicht). Dazu kommt ja noch, dass weitere template Parameter erlaubt sind (und so könnte das auch sonst eine Bibliothek handhaben, dass lediglich die typedefs funktionieren müssen).

    Vorwärtsdeklarationen im allgemeinen sind toll. Habe ja nicht umsonst was darüber geschrieben. 😉


  • Administrator

    Eine kurze Ergänzung zur Vorwärtsdeklaration von Klassen aus der Standardbibliothek:
    Dies gehört in den Bereich "gar nicht möglich". Es ist nämlich undefiniert.

    It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified.

    Gut, man kann sich nun darüber streiten, ob eine Vorwärtsdeklaration auch eine Deklaration ist, aber meiner Meinung nach schon 🙂

    Grüssli



  • Ja, wie gesagt würde ich das nicht auf die Standardbibliothek anwenden (ausser <iosfwd> gibts da keine portable Möglichkeit). Aber bei anderen Bibliotheken halte ich Template-Vorwärtsdeklarationen und entsprechende Typedefs durchaus für gerechtfertigt.


  • Administrator

    drakon schrieb:

    @dravere:
    Ich habe das auf Nexus bezogen und den typedef Vorwärtsdeklarationen.
    z.B Wie deklarierst du einen Iterator für basic_string vor?

    Das ist aber etwas völlig anderes, als das was Nexus gezeigt hat. Der basic_string<...>::iterator ist abhängig von der Klassendefinition.

    Grüssli



  • Stimmt. Schlechtes Beispiel, aber das Problem bleibt ja.

    Wenn jetzt plötzlich die Bibliotheksentwickler einen Parameter im typedef ändern wollen, dann muss man da auch nachziehen. Solch eine Abhängigkeit halte ich für schlecht.

    Und nein man kann sich nicht darüber streiten. 😉

    3.1/1 schrieb:

    A declaration (clause 7) introduces names into a translation unit or redeclares names introduced by previous
    declarations.

    Möglich ist es schon, aber wie du sagst es ist undefiniert.


  • Administrator

    drakon schrieb:

    Wenn jetzt plötzlich die Bibliotheksentwickler einen Parameter im typedef ändern wollen, dann muss man da auch nachziehen. Solch eine Abhängigkeit halte ich für schlecht.

    Und es gäbe einen Kompilerfehler. Innert 30 Sekunden hast du das angepasst und hast immer noch die extremen Vorteile davon, dass die Kompilierzeit extrem viel schneller ist 😉
    Wenn du die Version einer Bibliothek änderst, welche du verwendest, dann musst du meistens irgendwas anpassen gehen. Daher ordne ich dies ganz einfach dieser Arbeit zu. Ich sehe jedenfalls keinen extremen Nachteil und die Vorteile überwiegen.

    Aber klar, es gibt natürlich auch Fälle wo eine Vorwärtsdeklaration nicht nötig ist, weil womöglich die Entwickler der Bibliothek schon klug waren und entsprechende Header anbieten. Als Dogma ist es definitiv nicht zu verstehen, dass man immer Vorwärtsdeklarationen verwenden sollte, eher als eine Art von Richtlinie 😉

    drakon schrieb:

    Und nein man kann sich nicht darüber streiten. 😉

    3.1/1 schrieb:

    A declaration (clause 7) introduces names into a translation unit or redeclares names introduced by previous
    declarations.

    w000t! *holt eine Flasche Champagner*
    Endlich mal eine klare Definition! 😃 🤡

    Grüssli


  • Mod

    Dravere schrieb:

    drakon schrieb:

    Und nein man kann sich nicht darüber streiten. 😉

    3.1/1 schrieb:

    A declaration (clause 7) introduces names into a translation unit or redeclares names introduced by previous
    declarations.

    w000t! *holt eine Flasche Champagner*
    Endlich mal eine klare Definition! 😃 🤡

    Es gibt natürlich hierzu auch eine Ausnahme, d.h. eine Deklaration, die keinen Namen (wieder-)einführt...

    static union { int:0; };
    

    sinnlos zugegeben.


Anmelden zum Antworten