Kleines Text rpg und schon erste schwierigkeiten



  • hardtolearncplusplus schrieb:

    ...

    Du solltest dich wirklich erstmal um die Grundlagen kümmern. Einen Einstieg versuche ich mal zu geben:

    a) Klasse vs. Objekt
    Eine Klasse ist eine Schablone, ein Objekt die konkrete Ausprägung. Sprich eine Klasse Monster soll nur eine Schablone sein. Was haben deine Viecher...ähh Monster an Eigenschaften gemeinsam? So zum Beispiel wird jedes Monster wohl eine Bezeichnung und irgendwelche Kampfwerte haben (z.B. Stärke und Abwehr).

    Beispiel:

    class Monster
    {
      private:
        std::string name;
        int staerke;
        int abwehr;
      //...
    };
    

    Das bedeutet nicht das du jetzt separate Klassen für jedes Monster brauchst. Das "konkrete" Monster könnte beispielsweise ein Oger sein (name="oger", staerke=20, abwehr=10) oder eine Riesenschnecke (name="Riesenschnecke", staerke=10, abwehr=0). Dies alles sind aber Objekte, keine Klassen. Erst wenn du feststellst das du unterschiedliche Eigenschaften benötigst, brauchst du auch neue Klassen.

    Monster oger;
    Monster riesenschnecke;
    // Zwar sind beide Variablen (oger und riesenschnecke) Monster, können aber
    // unterschiedliche Werte haben.
    

    b) Methode
    Was eine Funktion ist, solltest du verstehen. Eine Methode ist eine Funktion einer Klasse. Der unterschied zu einer Funktion besteht darin, das eine Methode Zugriff auf alle Elemente der zugehörigen Klasse hat (Wenn es sich um eine statische Methode handelt: nur auf die statischen Member der Klasse).

    Da nicht-statische Methoden immer zu einem konkreten Objekt gehören (das man in der Methode auch über den this->Zeiger verwenden kann [ist nur nötig bei Namenskonflikten, wie ich es absichtlich im nachfolgenden Beispiel bei der Methode SetName zeige]).

    class Monster
    {
      private:
        std::string name;
        //...
    
      public:
        void SetName(std::string const & name)
        { // Der this-Zeiger verweist auf das entsprechende Objekt, dazu später mehr
          // Zuweisung des Parameter "name" zu der Membervariable "name"
          this->name = name;
        }
    
        std::string GetName() const
        { // this-Zeiger ist hier nicht nötig, da es hier keinen Konflikt
          // mit einer anderen Variable name gibt.
          return name;
        }
        //...
    };
    
    int main()
    {
      Monster oger;
      Monster riesenschnecke;
      oger.SetName("Oger");  // Setzen des Namens über eine Methode, dies betrifft
                             // aber nur "oger", nicht riesenschnecke
      riesenschnecke.SetName("Riesenschnecke");
      std::cout << oger.GetName() << std::endl
                << riesenschnecke.GetName() << std::endl;
      // ...
    }
    

    c) Konstruktor (& Destruktor)
    Es ist gang und gebe das man Variablen initialisiert. Zu nichts anderen ist der Konstruktor. Der Konstruktor wird am Anfang einmalig pro erstellten Objekt aufgerufen, und das Gegenstück, der Destruktor wird einmalig pro erstellten Objekt aufgerufen wenn es zerstört wird.

    int main()
    {
      Monster oger;              // Hier wird der Standardkonstruktor von der Klasse
                                 // Monster und dem Objekt oger aufgerufen.
      {
        Monster riesenschnecke;  // Hier wird der Standardkonstruktor von der Klasse
                                 // Monster und dem Objekt riesenschnecke aufgerufen.
      } // Hier wird riesenschnecke destruiert
    
    } // Hier wird oger destruiert
    

    Es kann mehr als einen Konstruktor geben (aber nur ein Destruktor, da es hier keine Aufrufparameter gibt). Als Standardkonstruktor wird ein Konstruktor bezeichnet der keine Parameter besitzt.

    class Monster
    {
      private:
        std::string name;
        int staerke;
        int abwehr;
    
      public:
        Monster()      // Standardkonstruktor, heißt wie Klasse
        : name(""),    // <- Initialiserungsliste
          staerke(0),
          abwehr(0)
        {
          // <- Konstruktorrumpf
        }
    
        Monster(   // Konstruktor mit Parametern
          std::string const & name,
          int staerke,
          int abwehr)
        : name(name),       // Zuweisung des 1. Parameters zur ersten Membervariable
          staerke(staerke),
          abwehr(name)
        {
        }
    
        ~Monster() // Destruktor in unseren Fall leer...
        {
        }
    };
    
    int main()
    {
      Monster oger("Oger", 20, 10);
      Monster riesenschnecke("Riesenschnecke", 10, 0);
    }
    

    Bedenke wieder das oger und riesenschnecke unterschiedliche Objekte sind. Jetzt hätten wir beide Objekte den oben genannten Vorgaben entsprechend angelegt.

    c.2) Konstruktor, Kopierkonstruktor, Zuweisungsoperator, Destruktor
    Noch ein Stück tiefergehend: Objekte kann man anlegen und zerstören, soweit so gut. Doch ebenso lassen sie sich zuweisen und kopieren.

    int main()
    {
      Monster oger("Oger", 20, 10); // Konstrukor
      Monster oger2(oger);          // Kopierkonstruktor
      Monster oger3 = oger;         // Achtung: Ebenso Kopierkonstruktor
      oger2 = oger;                 // Zuweisungsoperator
    } // <-- Destruktor(en)
    

    Automatisch legt der Compiler im Hintergrund auch alle davon an (sofern möglich), wobei der Standardkonstrukor nur generiert wird wenn kein anderer Konstruktor geschrieben wird. Und der automatische Kopierkonstruktor kopiert alle Werte 1:1 (Auchtung bei Zeigern, es wird der Wert des Zeigers, sprich die Adresse auf die er zeigt, und nicht das Objekt auf das er zeigt kopiert), der Zuweisungsoperator macht dies durch eine 1:1 Zuordnung.

    Wenn etwas davon nicht gewünscht ist, kann man die entsprechende Methode private deklarieren und verzichtet auf die Definition.

    Was der Kompiler automatisch generiert

    class Klassenname
    {
      public:
        Klassenname();                               // Konstruktor
        Klassenname(Klassenname const &);            // Kopierkonstruktor
        Klassenname& operator=(Klassenname const &); // Zuweisungsoperator
        ~Klassenname();                              // Destruktor
    }
    

    Mehr tippe ich aber erstmal nicht...

    cu André



  • hardtolearncplusplus schrieb:

    Aber mit den Klassen ist ja alles schön und gut, aber ich verstehe das mit public, privat und mit den Methoden nicht ganz.

    Also public bedeutet, dass der Anwender die Werte verändern kann?
    Privat bedeutet, dass nur eine interne Aufforderung die Werte ändern kann?

    Private Variablen und Funktionen der Klasse können im Normalfall nur intern aufgerufen bzw. bearbeitet werden, während öffentliche auch von ausserhalb manipulierbar sind. Beispielsweise ist es oft sinnvoll, die Membervariablen privat zu machen und dann über Get- und Set-Funktionen (Schnittstellen) diese zu manipulieren.

    class MeineKlasse
    {
       private: // privat: Variablen
          int x;
          float y;  // y wird z.B. nur intern benötigt, deshalb keine Schnittstellen für y
    
       public:  // öffentlich: Schnittstellen
          void SetX(int NewX);
          int GetX();
    };
    
    void MeineKlasse::SetX(int NewX)
    {
       x = NewX;
    }
    
    int MeineKlasse::GetX()
    {
       return x;
    }
    
    int main()
    {
       MeineKlasse A, B; // A und B sind Objekte oder Instanzen der Klasse
       A.x = 3;          // geht nicht, weil x privat ist. Genauso y...
       A.SetX(3);        // geht, weil die Funktion öffentlich (public) ist.
       int i = A.x;      // geht auch nicht
       int i = A.GetX(); // geht wieder
    }
    

    hardtolearncplusplus schrieb:

    Methoden sind... ähm damit kann man ja ka.^^

    Methoden nennt man die Funktionen, die im Zusammenhang mit Klassen bestimmte Aufgaben erfüllen. Meistens bezeichnet man damit Memberfunktionen, also die Funktionen, die in der Klasse deklariert sind. Im Beispiel vorher wären GetX() und SetX() Methoden.

    hardtolearncplusplus schrieb:

    Konstruktor weiß ich sowieso nicht. Obwohl ich mir das schon paarmal durchgelesen habe.

    Der Konstruktor ist diejenige Memberfunktion, die beim Erstellen einer Instanz aufgerufen wird. Eine Klasse kann mehrere Konstruktoren haben. Diese können beim Aufruf Parameter übernehmen, um die internen Variablen auf einen Startwert zu setzen. Konstruktoren ohne Parameter nennt man Standardkonstruktoren. Normalerweise wird ein Standardkonstruktor automatisch vom Compiler erstellt, sobald du jedoch irgendeinen eigenen Konstruktor definierst, nicht mehr.

    class MeineKlasse
    {
       private:
          int x;
          float y; 
       public:
          MeineKlasse(int NewX = 0);         // Konstruktor-Deklaration
          void SetX(int NewX);
          int GetX();
    };
    
    MeineKlasse::MeineKlasse(int NewX)   // Konstruktor-Definition
    {
       x = NewX;
       y = 0.f;
    }
    
    int main()
    {
       MeineKlasse A(3); // Aufruf des Konstruktors mit Parameter 3 -> A.x ist jetzt 3
       MeineKlasse B(); // parameterloser Aufruf -> Standardparameter 0 -> A.x ist 0
       MeineKlasse C; // auch parameterlos, auch hier wird der Standardkonstruktor genommen.
    


  • Nexus schrieb:

    //...
       MeineKlasse B(); // parameterloser Aufruf -> Standardparameter 0 -> A.x ist 0
    //...
    

    Ich glaube eher das er den B-Fall versucht als Funktionsaufruf zu werten...


  • Mod

    asc schrieb:

    Nexus schrieb:

    //...
       MeineKlasse B(); // parameterloser Aufruf -> Standardparameter 0 -> A.x ist 0
    //...
    

    Ich glaube eher das er den B-Fall versucht als Funktionsaufruf zu werten...

    fast. Es ist eine Funktionsdeklaration - hier passiert also schlicht und ergreifend gar nichts. Danke, C.


Anmelden zum Antworten