2 Klassen die sich gegenseitig benötigen - wie include endlosschleife umgehen?



  • Hallo!
    Ich habe 2 Klassen A und B. (ich habe mir angewöhnt den gesamten Code nur in eine Headerdatei zu schreiben, praktisch inline...)
    Sagen wir A hat die Funktion ChangeB(B * b); und B hat die Funktion ChangeA(A * a); das heisst jede Klasse muss die andere inkludieren. Das führt aber zu einer Endlosschleife. Wie kann ich das umgehen? Es ist nicht möglich dass ich einfach ChangeA bei A und ChangeB bei B mache...das müsst ihr einfach akzeptieren 🙂

    gibts nich sowas wie #ifninclude oder so? 😞

    Kevin



  • Du jagst dem falschen Problem hinterher.
    Das Problem ist keine Endlosschleife. Die wird durch die include-guards verhindert:

    // A.h
    #ifndef A_H
    #define A_H
    
    class A { ... };
    
    #endif
    

    Das Problem liegt darin, dass A B nicht kennt, und umgekehrt. Die einzige Möglichkeit dies zu umgehen ist eine forward declaration:

    // A.h
    #ifndef A_H
    #define A_H
    
    // forward
    class B;
    
    class A
    {
    public:
        void ChangeB(B*);
    };
    
    #endif
    

    Allerdings darf die Implementierung von ChangeB nicht im Header stehen. Solange eine Klasse nur forward deklariert ist, kannst du nämlich nur Zeiger und Referenzen darauf verwenden. Der Compiler weiß ja noch gar nicht wie B aussieht.



  • Und wenn die implementierung von ChangeB doch im Header steht? Weil dazu müsste ich jetzt alle Klassen in eine .cpp und .h aufteilen. 😞 Mir scheint das Problem aber doch eine Endlosschleife weil der Compiler sagt:
    --------------------Konfiguration: 2D Engine - Win32 Debug--------------------
    Kompilierung läuft...
    main.cpp
    c:\dxsdk\include\dinput.h: DIRECTINPUT_VERSION undefined. Defaulting to version 0x0800
    d:\eigene dateien\code\aktuell\cvertex_object.h(2) : warning C4182: Die #include-Verschachtelungsebene ist 360 tief; Endlosschleife moeglich
    d:\eigene dateien\code\aktuell\cvertex_object.h(2) : fatal error C1076: Compiler-Beschraenkung: Interne Heap-Grenze erreicht; Verwenden Sie /Zm, um eine hoehere Grenze anzugeben
    Zusatz.cpp
    d:\eigene dateien\code\aktuell\cvertex_object.h(2) : warning C4182: Die #include-Verschachtelungsebene ist 360 tief; Endlosschleife moeglich
    d:\eigene dateien\code\aktuell\cvertex_object.h(2) : fatal error C1076: Compiler-Beschraenkung: Interne Heap-Grenze erreicht; Verwenden Sie /Zm, um eine hoehere Grenze anzugeben
    Fehler beim Ausführen von cl.exe.

    2D Engine.exe - 2 Fehler, 2 Warnung(en)

    mhh



  • Sicher, dass du include-guards gesetzt hast?



  • Du hast definitiv keine include-guards benutzt.
    Wie gesagt, die Implementierung darf nicht inline sein.
    Du könntest die Implementierung natürlich wieder in den Header packen (außerhalb der Klasse), aber das ist kein guter Stil und der Übersicht nicht gerade förderlich . 🙂

    Du könntest auch dein Design überdenken, ob das so wirklich nötig ist.



  • Ich hab jetzt die Klasse umgeschrieben und bei beiden Klassen die Forwarddeclaration der anderen benutzt, jedoch heult dann der Compiler:
    --------------------Konfiguration: 2D Engine - Win32 Debug--------------------
    Kompilierung läuft...
    main.cpp
    c:\dxsdk\include\dinput.h: DIRECTINPUT_VERSION undefined. Defaulting to version 0x0800
    Zusatz.cpp
    CBoundingBox.cpp
    d:\eigene dateien\code\aktuell\cboundingbox.cpp(28) : error C2027: Verwendung des undefinierten Typs "CModelFormat"
    d:\eigene dateien\code\aktuell\cboundingbox.h(14) : Siehe Deklaration von 'CModelFormat'
    d:\eigene dateien\code\aktuell\cboundingbox.cpp(28) : error C2227: Der linke Teil von '->_header' muss auf Klasse/Struktur/Union zeigen
    d:\eigene dateien\code\aktuell\cboundingbox.cpp(28) : error C2228: Der linke Teil von '.iVertizes' muss eine Klasse/Struktur/Union sein
    CModelFormat.cpp
    Fehler beim Ausführen von cl.exe.

    2D Engine.exe - 3 Fehler, 0 Warnung(en)

    (meine 2 Klassen sind nicht A und B sondern CModelFormat und CBoundingBox 😉 )

    Kevin

    Edit: habs jetzt - hatte nur vergessen in der .cpp die andere Klasse zu inkludieren! DANKE und nen guten Rutsch ins neue Jahr 😉



  • Du hast den Code immer noch im Header stehen. 🙄

    Ich habe doch gesagt, dass du nur Zeiger und Referenzen auf die Klasse verwenden kannst und nicht die Klasse selber.

    A.cpp muss ungefähr so aussehen:

    #include "A.h"
    // Die foward-declaration muss ersetzt werden durch die vollständige Deklaration von B, sonst kannst du B nicht benutzen:
    #include "B.h"
    
    void A::ChangeB(B* b)
    {
      // Das geht, weil der Code nicht inline ist und weil #include "B.h" davor ist:
      b->machWas();
    }
    


  • Hi,

    irgendwie hab ich den Eindruck, Surkevin liest gar nicht, was man schreibt... nicht nur hier, sondern auch mehrfach unter Spiele- & Grafik-Programmierung!

    ChrisM



  • mh bist dir da sicher? Ich nicht...meine Fragen in Spiele Grafikprogrammierung gingen hauptsächlich um Mathe, dessen Stoff ich erst in 3 Jahren lernen werde. Da ich zu Vektoren noch keine Vorkenntnisse hatte könnt ihr mir die daraus resultierenden Begriffsstutzigkeiten nicht verübeln. Ich hab oben außerdem meinen Post editiert (auch @ cd9000) - es funktioniert jetzt ja. Nehmt mir doch alles übel... frohes neues jahr



  • Da wir schon gerade dabei sind.
    In früheren Versionen von VC (bis Version 6 glaub ich)
    Erzeugte er Klassen ungefähr so:

    // Headerdatei
    #ifndef FOO_H
    #define FOO_H
    
    class Foo
    {
    // ...
    };
    
    #endif
    

    Ab VC.NET folgendermaßen:

    // Headerdatei
    #pragma once
    
    class Foo
    {
    // ...
    };
    

    Was ist der Unterschied zwischen den Defines und der pragmaanweisung in den neueren Versionen?
    Und was sollte man verwenden.

    PS: Iss jetzt nicht unbedingt ne C++ spezifische Sache, aber ich will nicht unbedingt einen extra Thread aufmachen 🤡

    wie kann man das aussehen einer Autometisch generierten C++-Klasse in VC.NET 2003 ändern? (Ich will nicht, dass er im Parameterlosen Konstruktor void hinenschreibt)

    Schönen Tag noch.



  • das pragma ist idr schneller, das define-konstrukt portabel.
    pragma ist compilerspezifisches, und bei once, nehm ich mal an, wird der compiler sich hüten, die datei ein zweites mal zu öffnen. bei define muss der (pre)compiler jedesmal die datei neu öffnen (wenn er nicht besonders intelligent ist)
    wie du es änderst weiß ich nicht, weil ich das studio nicht verwende. frag mal in den entsprechenden foren nach.



  • Danke für die Antwort. Werde mal im VisualC++ Forum nachfragen.



  • davie schrieb:

    das pragma ist idr schneller, das define-konstrukt portabel.

    Wobei man die Geschwindigkeit mit sogenannten redundanten Include-Guards (macht eigentlich nur bei großen Projekten sinn) deutlich verbessern kann.
    Redundante Include-Guards umschließen jede include-Anweisung mit einem extra Include-Guard:

    #ifndef A_HEADER_NAME_INCLUDED
    #define A_HEADER_NAME_INCLUDED
    
    #ifndef B_HEADER_NAME_INCLUDED
    #include "b.h"
    #endif
    
    #ifndef C_HEADER_NAME_INCLUDED
    #include "c.h"
    #endif
    
    #ifndef IOSTREAM_H_INCLUDED
    #include <iostream>
    #define IOSTREAM_H_INCLUDED
    #endif
    
    class A
    {
    B b;
    C c;
    ...
    };
    
    #endif
    

    So wird jede Header-Datei wirklich nur einmal geöffnet, egal wie oft sie in eine UE inkludiert wird. Das funktioniert natürlich nur, wenn man eine Namensstrategie für seine Include-Guards hat und diese auch konsequent anwendet. Für Standard-Header sollte man dann natürlich ebenfalls Guards definieren (siehe Beispiel).


Anmelden zum Antworten