Header überkreuz einbinden???



  • Hallo.

    Ich habe zwei Klassen-Header in zwei verschiedenen Dateien und jede soll eine Instanz von der jeweils anderen haben:

    Beispiel Nr.1 :

    classA.h :

    #include "classB.h"
    
    class A{
    public:
      A();  
      B klasseB;
    }
    

    classB.h :

    #include "classA.h"
    
    class B{
    public:
      B();
      A klasseA;
    }
    

    So gehts schonmal garnicht, da der Prä-Proz sich nun in einer Endlosschleife befindet.

    Beispiel Nr. 2 :

    classA.h :

    #ifndef _CLASS_A_H_
    #define _CLASS_A_H_
    
    #include "classB.h"
    
    class A{
    public:
      A();  
      B klasseB;
    }
    
    #endif
    

    classB.h :

    #ifndef _CLASS_B_H_
    #define _CLASS_B_H_
    
    #include "classA.h"
    
    class B{
    public:
      B();
      A klasseA;
    }
    
    #endif
    

    So gehts aber leider auch nicht, da nun nur die Klasse vom Prä-Proz erkannt wird welche ich zuerst einbinde.

    Fazit :

    Ich sitze nun schon seit Stunden an diesem Problem. Würde schon gerne der Übersichtlichkeit wegen die Klassen in verschiedene Dateien schreiben.

    Da ich aus der Java-Ecke komme und da ein solches Problem einfach nicht auftreten kann(vermisse schon den garbage-collector^^) brauche ich nun dringend: HILFE !!! 😉 🙂

    Würde mich sehr über einen Tipp oder eine Lösung freuen^^

    beste Grüße
    grid



  • grid schrieb:

    Ich habe zwei Klassen-Header in zwei verschiedenen Dateien und jede soll eine Instanz von der jeweils anderen haben:

    Hallo grid,

    das geht nicht. Ist ja auch irgendwie logisch, wenn man sich überlegt was das bedeutet. Wenn eine Schachtel eine (zwangsläufig kleinere) Schachtel enthält und diese soll wiederum die größere enthalten, so geht das einfach nicht.

    Im Übrigen ist die gegenseitige Abhängigkeit zweier Klassen immer auch ein Designfehler (auch in Java). Erzähl doch mal, was das für Klassen werden sollen und ein bisschen zur Aufgabenstellung.

    Gruß
    Werner



  • Hallo Werner.

    Also weg von den Beispielen und hin zum eigentlichen Problem^^ :

    Ich möchte in einer KlasseA eine Memberfunction einer Instanz der KlasseB als CallBack aufrufen.

    ClassA.h:

    #ifndef _CLASS_A_H_
    #define _CLASS_A_H_
    
    #include "ClassB.h"
    
    typedef void (ClassB::*callBackFunc)(std::string s);
    
    class ClassA{
    public:
    ClassA();
    void registerCallBackFuncFromClassB(callBackFunc f);
    };
    
    #endif
    

    ClassB.h:

    #ifndef _CLASS_B_H_
    #define _CLASS_B_H_
    
    #include "ClassA.h"
    
    class ClassB{
    public:
    ClassB();
    void callMeBackFromClassA(std::string s);
    };
    
    #endif
    

    Da es das selbe Problem mit dem Header-Files über kreuz einbinden ist dachte ich die ersten beiden Beispiele wären vielleicht etwas einfacher zu verstehn.



  • hmmmm...

    ...Habe gerade in den C++ Projekt-Einstellungen unter "Preprocessor" den Punkt "Generate Preprocessed File" von "No" auf "/P" (With Line Numbers) umgestellt. Nun scheint er den Code ohne Fehler zu übersetzten, braucht aber dafür nun extrem lange da ja eine Datei erzeugt wird....

    ...ob das die optimale Lösung ist? Was glaubt ihr?

    und was ist eigentlich :

    #pragma once
    

    beste Grüße
    grid


  • Administrator

    #pragma once gehört zu Visual Studio und ist die Abkürzung für die Header-Guards. Also anstatt das ifndef define code endif.

    Zu deinem Problem. Ich verstehe zwar dein Beispiel überhaupt nicht, aber ich glaube es würde reichen, wenn du es mit forward declaration versuchen würdest. Anstatt eines Objektes zu speichern, speicherst du einfach nur einen Zeiger.

    Mal ein kurzes Beispiel:

    // A.hpp
    
    #pragma once
    
    class B;
    
    typedef void (B::*Function)();
    
    class A
    {
    public:
        void register_function(Function BFunction) { m_BFunction = BFunction; };
        void call_callback(B* pB);
    
    private:
        Function m_BFunction;
    }
    
    // A.cpp
    
    #include "A.hpp"
    #include "B.hpp"
    
    void A::call_callback(B* pB)
    {
        (pB->*m_BFunction)();
    }
    
    // B.hpp
    
    #pragma once
    
    class B
    {
    public:
        void test() { std::cout << "test" << std::endl; };
    }
    
    // main
    
    #include "A.hpp"
    #include "B.hpp"
    
    int main()
    {
        A a;
    
        a.register_function(&B::test);
    
        B b;
        a.call_callback(&b);
    
        return 0;
    }
    

    Ungetestet.
    Ich hoffe das hilft weiter.

    Grüssli



  • Hallo Dravere und Danke für deine Antwort.

    Also wenn ich einfach nur vor

    typedef void (ClassB::*callBackFunc)(std::string s);
    

    ein

    class ClassB;
    

    schreibe und später in einer anderen .h - Datei eine class ClassB; definiert wird macht das nichts ???

    Wird dann einfach die Klasse überschrieben ???

    Also:

    class ClassB;
    typedef void (ClassB::*callBackFunc)(std::string s);
    

  • Administrator

    Überschreiben ist vielleicht etwas der falsche Ausdruck, aber eigentlich ja.

    Man gibt einfach bekannt, dass es mal eine Klasse geben wird, welche so heissen wird. Damit kann man dann Zeiger und Referenzen erstellen, allerdings ohne sie zu verwenden, bzw. zu dereferenzieren.
    Für die Verwendung (Aufruf von Funktionen, Erstellung von Objekten, Zuweisung von Werten usw.) muss natürlich dann die Klasse vollständig bekannt sein.

    Ich bin vielleicht nicht der Oberguru im erklären, daher falls dich noch genauer informieren möchtest, suche mal nach Vorwärtsdeklaration oder Forward Declaration. Gibt sicher auch Themen dazu in diesem Forum 😉

    Grüssli



  • Scheint alles zu klappen^^

    Vielen Dank !

    PS: Hab noch ne Frage, mach aber da ein neues Thema auf 😉



  • Ein Callback macht ja eigentlich nur so richtig Sinn, wenn man verschiedene Funktionen registrieren kann. Du könntest also auch eine Inerface-Klasse anlegen, die in beiden Klassen bekannt ist:

    struct ICallback
    {
        virtual void callback(const std::string& str) = 0;
    };
    
    class A
    {
        public:
        void registerCallback(const ICallback& callback);
    };
    
    class B : public ICallback
    {
        public:
        void callback(const std::string& str);
    };
    //...
    

    Alle Klassen, die ein passendes Callback unterstützen sollen, implementieren dann das Interface. Und Objekte dieser Klassen können dann registriert werden.



  • Hallo Tachyon,
    auch ne sehr gute Idee. Danke^^


Anmelden zum Antworten