Anfängerproblem: Klassen benötigen gegenseitig eine Deklaration der jeweils Anderen



  • Die Forward-Declarations dürfen nciht innerhalb der Klasse stehen, damit deklarierst du Unterklassen Spieler::Ball bzw. Ball::Spieler. Die friend-deklarationen beziehen sich dann automatisch auf diese Unterklassen, so dass z.B.
    [c]void Game::Kollisionsabfrage(Ball &Ball, **Bal::**Spieler &Spieler);[/c] ein friend von Ball wäre. Pack die Forward-Deklarations in den Scope wo sie hingehören, d.h. außerhalb der Klassen, innerhalb des Namespace:

    namespace Game
    {
        class Spieler; //Fwd-Decl
        class SDL_Surface; //Fwd-Decl;
    
        class Ball
        {
    
            friend void Game::Kollisionsabfrage(Ball &Ball, Spieler &Spieler);
    //...
    


  • Ich komme nochmal auf mein "Abhängigkeiten reduzieren"-Punkt. Die Kollisionsabfrage bei Dir greift direkt auf geschützte Objekt-Elemente von Ball und Spieler zu. Das ist schlecht. Du solltest dafür Funktionen zur Verfügung stellen, die man in kollisionsabfrage() einfach aufrufen kann. Damit ersparst Du Dir auch die sonst nötigen friend-Deklarationen.

    Eventuell kannst Du die Objekteigenschaften, die Du in Kollisionsabfrage benutzt, abstrahieren. ZB so:

    struct Vec2d
    {
      double xy[2];
    
      explicit Vec2d(double x=0, double y=0)
      { xy[0]=x; xy[1] = y; }
    
      double&       x()       {return xy[0];}
      double const& x() const {return xy[0];}
      double&       y()       {return xy[1];}
      double const& y() const {return xy[1];}
    };
    
    class Objekt2D
    {
    protected:
      ~Objekt2D() {} // alternativ: public und virtuell
    public:
      Vec2d position() const = 0;
      Vec2d groesse() const = 0;
      Vec2d geschwindigkeit() const = 0;
      void position(Vec2d p) = 0;
      void geschwindigkeit(Vec2d p) = 0;
    };
    
    class Ball : public Objekt2D
    {
      ...
    };
    
    class Spieler : public Objekt2D
    {
      ...
    };
    
    void kollisionsabfrage(Objekt2D & o1, Objekt2D & o2)
    {
      ...
    }
    

    ...ist ja nur ein Vorschlag.

    Gruß,
    SP



  • class Objekt2D
    {
    protected:
      ~Objekt2D() {} // alternativ: public und virtuell
    public:
      Vec2d position() const = 0;
      Vec2d groesse() const = 0;
      Vec2d geschwindigkeit() const = 0;
      void position(Vec2d p) = 0;
      void geschwindigkeit(Vec2d p) = 0;
    };
    

    Nachtrag: Vor jede Funktionsdeklaration im public-Bereich gehört noch ein "virtual".

    Diese Klassendefinition könntest Du in einen eigenen Header packen, der dann für die "Kollisionsabfrage"-Funktion eingebunden wird, so dass diese nichts von Spieler oder Ball wissen muss.

    Zumindest ist es erstrebenswert, die Programmteile weitestgehend zu entkoppeln.



  • Sebastian Pizer schrieb:

    struct Vec2d
    {
      double xy[2];
    

    Warum Du auch? Von Dir hätte ich

    struct Vec2d
    {
      double x;
      double y;
    

    erwartet.



  • Um ehrlich zu sein habe ich keine Ahnung, was:

    explicit Vec2d(double x=0, double y=0)
      { xy[0]=x; xy[1] = y; }
    
      double&       x()       {return xy[0];}
      double const& x() const {return xy[0];}
      double&       y()       {return xy[1];}
      double const& y() const {return xy[1];}
    

    macht.

    Vor jede Funktionsdeklaration im public-Bereich gehört noch ein "virtual".

    Ich brauch doch nur virtuelle Funktionen, wenn die Klasse erbt oder vererbt, oder?

    Aber euch allen schonmal vielen Dank für den Hinweis mit den Klassendeklarationen in den Klassen. 🙂



  • Nochmal ganz kurz zwei Sachen^^.

    pumuckles Problemlösung hat wieder ein neues Problem hervorgerufen:

    namespace Game
    {
        class Spieler; //Fwd-Decl
        class SDL_Surface; //Fwd-Decl;
    
        class Ball
        {
    
            friend void Game::Kollisionsabfrage(Ball &Ball, Spieler &Spieler);
    //...
    
    C:\...\Kollisionsabfrage.cpp|66|error: invalid use of undefined type `struct Game::SDL_Surface'|
    

    SDL_Surface darf nicht zum namespace Game gehören, sonst funktioniert es nicht mehr. Ich habs jetzt einfach außerhalb geschrieben und das geht.

    EDIT: Und das zweite Problem hat sich mir grad auch erschlossen. Nur irgendwie bin ich zu blöd, es richtig zu schreiben.

    Ich muss der Elementfunktion (heißt doch so, oder? 😕 ) Reset ein Objekt mitgeben. Ich wollte das so machen, wobei das Objekt auch Ball heißt:

    Ball::Ball.Reset(SpielfeldversatzX, SpielfeldX, SpielfeldversatzY,
                             SpielfeldY, 2);
    

    Der Compiler meint dazu nur:

    C:\...\Kollisionsabfrage.cpp|56|error: expected unqualified-id before '.' token|
    

    Nehme ich anstatt Ball irgendeinen anderen Begriff als Objektnamen, um den Compiler nicht zu verwirren 😉 , dann schreibt er stattdessen:

    C:\...\Kollisionsabfrage.cpp|56|error: `Kugel' is not a member of `Game::Ball'|
    

    Wie schreibe ich denn eine Elementfunktion im namespace so, dass der Compiler versteht, was ich meine?



  • Little Programmer schrieb:

    Ball::Ball.Reset(SpielfeldversatzX, SpielfeldX, SpielfeldversatzY,
                             SpielfeldY, 2);
    

    Gibt es den namespace Ball? Und was meinst du mit "ein Objekt mitgeben"?
    Wenn du ein Objekt einBall vom Typ Game::Ball hast, rufst du die Elementfunktion Reset so auf: einBall.Reset(...);

    Es ist übrigens eine schlechte Idee, Objekte wie ihre Klasse zu nennen, das führt nur zu Problemen.
    Besser so:

    Ball ball; //Objektnamen immer klein schreiben
    TBall Ball; //Borland-Art
    BG_Ball Ball; //Projektkürzel voranstellen
    


  • volkard schrieb:

    Sebastian Pizer schrieb:

    struct Vec2d
    {
      double xy[2];
    

    Warum Du auch? Von Dir hätte ich

    struct Vec2d
    {
      double x;
      double y;
    

    erwartet.

    *mit-den-Achseln-zuck*

    Es wär doch nett, wenn man die Komponenten per Index ansprechen könnte.

    Gruß,
    SP



  • Sebastian Pizer schrieb:

    Es wär doch nett, wenn man die Komponenten per Index ansprechen könnte.

    Einfach nur so, ohne einen Algorithmus im Sinn, wo man das brauchen könnte.



  • volkard schrieb:

    Sebastian Pizer schrieb:

    Es wär doch nett, wenn man die Komponenten per Index ansprechen könnte.

    Einfach nur so, ohne einen Algorithmus im Sinn, wo man das brauchen könnte.

    Einfach nur so, wissend dass es durchaus einen Algorithmus geben kann den man grade nicht im Sinn hat. Und dass es auf die Performance, den Speicherverbrauch und das Interface keinen Einfluss hat. Nur auf die Erweiterbarkeit eben.



  • pumuckl schrieb:

    Einfach nur so, wissend dass es durchaus einen Algorithmus geben kann den man grade nicht im Sinn hat.

    Dann mußt Du aber auch Nägel mit Köpfen machen und eine Fahrrad& getFahrrad() anbieten, einfach nur so, weil es einen Algorithmus geben könnte, der eun Fahrrad brauchen könnte. Würde ich nie machen.



  • volkard schrieb:

    Dann mußt Du aber auch Nägel mit Köpfen machen und eine Fahrrad& getFahrrad() anbieten, einfach nur so, weil es einen Algorithmus geben könnte, der eun Fahrrad brauchen könnte.

    Nein, im Gegensatz zu der Entscheidung 2 x double <-> double[2] wäre das ein Eingriff ins Interface der Klasse, der zu dem Zeitpunkt (noch) nicht nötig ist.


  • Administrator

    @pumuckl & Sebastian Pizer,
    Wieso nicht eher sowas?

    struct Vec2d
    {
      double x;
      double y;
    
      double& operator [](std::size_t index)
      {
        assert(index < 2);
        return (index == 0 ? x : y);
      }
    
      double operator [](std::size_t index) const
      {
        assert(index < 2);
        return (index == 0 ? x : y);
      }
    };
    

    Würde ich irgendwie als schöner empfinden 😉

    Grüssli



  • Dravere schrieb:

    @pumuckl & Sebastian Pizer,
    Wieso nicht eher sowas?

    Die obige Version läßt sich ohne Mühe und Umdenken auch für drei oder vier Dimensionen nehmen. Und sie kostet gar nichts, was man von einem if nicht sagen kann. Wenn man Indexzugriffe erlauben will, ist sie wohl die hübscheste.



  • Nochmal zu meinem Problem^^.

    Ich habe den Aufruf jetzt geändert, jetzt erzeugt er keinen Fehler mehr. Doch plötzlich passt dem Compiler wieder alles nicht. Ich könnte mir die Haare ausreißen! 😡

    Zu der Definition der Funktionen in der Klasse Spieler, hier:

    #ifndef _SPIELER_H_
    #define _SPIELER_H_
    #include "Spieler.h"
    #endif
    
    Spieler::Spieler(int* SpielfeldversatzX, int* SpielfeldX, int* SpielfeldversatzY,
                     int* SpielfeldY, int AILevel)
                   : dPosX(*SpielfeldversatzX + *SpielfeldX/7 - *SpielfeldX/40),
                     dPosY(*SpielfeldversatzY + *SpielfeldY/2 - *SpielfeldX/8),
                     w(*SpielfeldX/82.6), //82 zwei drittel
                     h(*SpielfeldX/16.5), //16 acht fünfzehntel
                     Punkte(0),
                     AILevel(AILevel),
                     dSpeedY(0){}
    ...
    

    Meint er plötzlich:

    C:\...\Spieler.cpp|11|error: `Spieler' has not been declared|
    

    Und halt alle daraus resultierenden Fehler. Spieler.h ist doch eingebunden. Wieso fängt der Compiler jetzt damit an? Bis eben waren die flasch formulierten Reset()-Aufrüfe die beiden letzten Fehler gewesen.



  • Deine Include-Guards gehören imho in die Header-Datei...



  • asdasdasd schrieb:

    Deine Include-Guards gehören imho in die Header-Datei...

    d.h.:

    /* *.cpp */
    
    #include "Spieler.h"
    
    Spieler::Spieler(int* SpielfeldversatzX/*....*/)
    {}
    
    /* *.h */
    
    #ifndef H_SPIELER_INCLUDED_
    #define H_SPIELER_INCLUDED_
    
    class Spieler
    {
      /*...*/
    };
    
    #endif //#ifndef H_SPIELER_INCLUDED_
    

    bb



  • Habs jetzt geändert, aber das ist ja nur Formsache und ändern nichts an dem Problem, dass Spieler.h einfach ignoriert wird.



  • Little Programmer schrieb:

    Habs jetzt geändert, aber das ist ja nur Formsache und ändern nichts an dem Problem, dass Spieler.h einfach ignoriert wird.

    Nein, ist es nicht. Wenn du die Guards auch im Header hast, dann wird dessen Inhalt auch nicht eingebunden. Ist dir wirklich klar, was #define macht?



  • Wenn ich den Header nur mit Guards einbinde, wird er auch nicht eingebunden. ist nur mehr schreibarbeit mit dem selben Resultat. An den Fehlern hat sich auch gar nichts geändert.


Anmelden zum Antworten