"Deadlock" in Klassendeklarationen



  • So nun hab ich, das Problem liegt wohl daran, dass du keine Guards kennst!



  • vorwärts deklaration

    class A;
    
    class B
    {
    public:
       A a;
    };
    
    class B;
    class A
    {
    public:
       B b;
    };
    


  • Was sind denn Guards? Google spuckt nix brauchbares aus

    Hast du ein Beispiel dazu?



  • Hallo

    Nein Include Guards helfen da nicht weiter (und ich vermute das PHPFreak diese bereits eingebaut hat)
    Sondern wie BigNeal bereits geschrieben hat, das Zauberwort heißt Forward Declaration. Allerdings erzwingt das die Verwendung von Zeigern oder Referenzen bei den Membern. Siehe Suchfunktion hier im BCB-Forum und im C++ Forum.

    bis bald
    akari



  • Ich verwende schon ausschließlich Pointer als Attribute. Trotzdem komme ich so nicht weiter...

    Das Problem ist, dass ich nicht nur den Namen der Klasse verwende (z.b. so: methode(B* objekt)), sondern dass ich auch auf dessen Methoden zugreifen muss



  • Forward Deklartion hatte ich vor dem Editeren, PHPfreak hat dann darauf geantwortet, ich hab nicht mitbekommen und geändert auf die Guards ;o

    PHPfreak schrieb:

    Was sind denn Guards? Google spuckt nix brauchbares aus

    Hast du ein Beispiel dazu?

    Oben. Die #ifndef #define #endif Gerüst.



  • Ah ok, danke für den Hinweis!

    Aber wie löse ich mein Problem? 😃 😃



  • Hallo

    Indem du Forward Declaration verwendest. Zeiger benutzen reicht nicht aus.

    bis bald
    akari



  • Aber das hab ich doch... Mal ein Beispiel:

    class Controller;
    
    //Die Klasse Computer wird von der abstrakten Klasse Spieler abgeleitet
    //und repräsentiert einen PC-Spieler.
    //Es gibt verschiedene Schwierigkeitsstufen
    
    class Computer : public Spieler
    {
        private:
          unsigned int schwierigkeit; // 1 = Einfach, 2 = Schwer
    

    Aber in der Klasse Computer wird die Methode getCounter() von Controller aufgerufen. Und die kennt er nicht



  • Da musst du leider mehr zeigen. Aus deinem Beispiel seh ich keine Probleme.



  • Ok, hier die gesamte Datei:

    class Controller;
    
    //Die Klasse Computer wird von der abstrakten Klasse Spieler abgeleitet
    //und repräsentiert einen PC-Spieler.
    //Es gibt verschiedene Schwierigkeitsstufen
    
    class Computer : public Spieler
    {
        private:
          unsigned int schwierigkeit; // 1 = Einfach, 2 = Schwer
    
        public:
          Computer() : Spieler("Computer")
          {
            this->schwierigkeit = 2;   //Standart = schwer
          };
    
          Computer(int s) : Spieler("Computer")
          {
            this->schwierigkeit = s;     //Schwierigkeit als Konstruktorparameter
          }
    
          AnsiString getType()
          {
            return "Computer";           //Spielertyp zurückgeben
          }
    
          unsigned int getSchwierigkeit()
          {
            return this->schwierigkeit;  //Schwierigkeit zurückgeben
          }
    
          void setSchwierigkeit(unsigned int s)     //Schwierigkeit setzen
          {
            if(s == 1 || s == 2)
            {
              this->schwierigkeit = s;
            }
            else
            {
              throw "Diese Schwierigkeit existiert nicht!";   //Exception
            }
          }
    
          bool move(SpielfeldModel* model, Feld* feld, Controller* controller)
          {
           Sleep(300);
           switch(this->getSchwierigkeit())
           {
            case 1:  this->pcEasy(model);                    //PC spielt mit pcEasy()
                     break;
            case 2:  this->pcDifficult(controller, model);               //PC spielt mit pcDifficult()
                     break;
            default: throw "Crashed due to wrong difficulty";
                     break;
            }
    
           return true;
          }
    
          /*
          Die einfache Spielmethode setzt an einer zufälligen Stelle im Spielfeld
          einen Stein, falls das Feld frei ist.
          */
          void pcEasy(SpielfeldModel* model)
          {
           Feld *feld = NULL;  //Temporärer Speicher für Feld
           int x = 0, y = 0;   //Koordinaten des PC-Zugs
    
           randomize();        //Zufallsgenerator starten
    
           do
           {
            x =  random(CONST__anzX);                //zufällige x-Koordinate
            y =  random(CONST__anzY);                //zufällige y-Koordinate
            if(model->isValidCoord(x,y))       //Prüfen, ob Koordinate erlaubt ist
            {
             feld = model->getFeld(x,y);      //Das Feld (x|y) holen
            }
           }
           //solange das Spiel nicht unentschieden, beendet oder das Feld voll ist.
           while(feld != NULL && !feld->isEmpty());
    
           feld->setPlayer(this);            //Dem gefundenen Feld den Besitzer zuweisen
          }
    
          /*
          Die schwere Spielmethode versucht, eigene Kombinationen zu erstellen und zu
          vervollständigen.
          Außerdem findet diese Methode die "Gewinnversuche" des Gegners und versucht,
          diese zu verhindern.
          */
          void pcDifficult(Controller* controller, SpielfeldModel* model)
          {
           Feld *feld = NULL;  //Temporärer Speicher für Feld
    
           //Die ersten beiden Züge sollen nicht mit der KI gemacht werden,
           //weil der PC sonst am Anfang immer den gleichen Zug macht, wenn der Mensch
           //das auch macht.
           if(controller->getCounter() <= 1)
           {
            this->pcEasy(model);
            return;         //Abbruch, da Zug gemacht wurde.
           }
    
           //Als erstes wird versucht, eigene 3er-Kombinationen zu erzeugen
           feld = controller->findCombinations(controller->getCurrentPlayer(), 2);
    
           if(feld == NULL)
           { //Dann wird versucht, gegnerische 2er zu blockieren, so dass keine 3er entstehen
             feld = controller->findCombinations(controller->getNextPlayer(), 2);
           }
    
           if(feld == NULL)
           { //Andernfalls werden aus eigenen 1ern 2er gemacht
             feld = controller->findCombinations(controller->getCurrentPlayer(), 1);
           }
    
           if(feld == NULL)
           {  //Sonst gegnerische 1er blockieren, so dass keine 2er entstehen
              feld = controller->findCombinations(controller->getNextPlayer(), 1);
           }
    
           if(feld == NULL)
           { //Sollte nichts geklappt haben, wird die Zufallsmethode versucht
             this->pcEasy(model);
           }
           else
           { //Wenn die KI zugeschlagen hat, wird das Feld belegt
             feld->setPlayer(this);
           }
          }
    };
    


  • Sieht so aus, als wäre mein Problem wirklich unlösbar 😃
    Ich hab jetzt nochmal rumprobiert und es nicht hingekriegt



  • du musst mehrere dateien verwenden.
    statt alles inline zu definieren, trennst du es
    in deklaration und definition:

    in a.h

    #includeguard
    
    class b;
    class a
    {
    public:
        void func(b *);
    };
    

    in a.cpp

    #include "a.h"
    #include "b.h"
    
    a::func(b *ptr)
    {
      ptr->b_only();
    }
    


  • So geht es bei zyklischen Abhängigkeiten:

    // App.cpp
    #pragma hdrstop
    
    #include <tchar.h>
    #include <iostream>
    #include "A.h"
    #include "B.h"
    
    using namespace std;
    
    #pragma argsused
    int _tmain(int argc, _TCHAR* argv[])
    {
    
    	A* a = new A();
    	B* b = new B();
    
    	a->where(b);
    	b->where(a);
    
    	cin.get();
    	return 0;
    }
    
    // A.h
    #ifndef AH
    #define AH
    
    #include "B.h"
    
    class B;
    
    class A
    {
    public:
    	void where(B* b) const;
    };
    
    #endif
    
    // A.cpp
    #pragma hdrstop
    
    #include <iostream>
    #include "A.h"
    
    using std::cout;
    using std::endl;
    
    void A::where(B* b) const
    {
    	cout << "B is there:" << b << endl;
    }
    #pragma package(smart_init)
    
    // B.h
    #ifndef BH
    #define BH
    
    #include "A.h"
    
    class A;
    
    class B
    {
    public:
    	void where(A* a) const;
    };
    
    #endif
    
    //B.cpp
    #pragma hdrstop
    
    #include<iostream>
    #include "B.h"
    
    using std::cout;
    using std::endl;
    
    void B::where(A* a) const
    {
    	cout << "A is there:" << a << endl;
    }
    #pragma package(smart_init)
    


  • Sorry Zeus, aber du hast immer noch zyklische Abhängigkeiten:
    A.h bindet B.h ein und umgekehrt!!!

    #include "A.h" bzw. #include "B.h" müssen aus den jeweiligen Headerdateien gelöscht werden, und (wie akari schon zweimal schrieb) nur Vorwärtsdeklarationen (forward declarations) verwendet werden:

    // A.h
    #ifndef AH
    #define AH
    
    class B;
    
    class A
    {
    public:
        void where(B* b) const;
    };
    #endif
    
    // B.h
    #ifndef BH
    #define BH
    
    class A;
    
    class B
    {
    public:
        void where(A* a) const;
    };
    
    #endif
    

    Nur wenn A ein Objekt von B enthält (als Member) wird die komplette Klassendeklaration von B benötigt (und damit ein #include "B.h").



  • Ist das ein Problem?



  • So, hab jetzt alles in Header-Dateien und in "Body"-Dateien aufgesplitetet.
    Das war vielleicht ne Drecksarbeit, aber sie hat nichts gebracht.

    Ich include als Beispiel so:

    include "controller.h"
    include "controller.cpp"
    

    In Controller.h ist die Klasse definiert und in controller.cpp sind die Methoden ausformuliert. Leider erhalte ich jetzt diese schöne Fehlermeldung:

    [C++ Fehler] controller.cpp(1): E2090 Qualifizierer 'Controller' ist kein Name einer Klasse oder einer Struktur

    bool Controller::columnIsFull(int column)
    {
      return this->getHighestField(column) == NULL;
    }
    

    Wie krieg ich das denn jetzt hin?
    Müsste doch theoretisch so gehen, oder nicht?



  • Hallo

    include "controller.cpp"

    Warum das denn? Das Einbinden von Implementations-Dateien (im Gegensatz zu Header-Dateien) ist sehr unüblich und meistens ein Fehler.

    Also ich werde dir dann auch noch die Verwendung der Suchfunktion abnehmen, und finde gleich diesen Thread, wo ich den Einsatz von Forward Declaration an einem Beispiel erläutere.

    bis bald
    akari



  • Bin ich jetzt komplett bescheuert?

    Ich bin mir sicher, dass ich die Forward Declarations richtig angwendet habe.

    Die includes habe ich jetzt übrigens entfernt. Hat nichs an der Situation geändert. Werden cpp-Dateien autmatisch includiert oder warum braucht man die nicht?

    Also in PHP gibts sowas nicht 😃 Vielleicht ist das daher für mich grad alles so schwer verständlich



  • cpp Dateien werden nicht inkludiert sondern vom Compiler einzeln in Objektcode überstetzt. Der Linker fügt dann zu Schluss alles zusammen.
    Wenn es immer noch nicht geht, dann hast du wahrscheinlich noch Definitionen in deinen Headerdateien drin. In die Headerdateien gehören bei dir nur Deklarationen.
    Zeig bitte mal deinen Code.


Anmelden zum Antworten