enums in Konstruktoren verwenden



  • Hallo!
    Ich habe eine Klasse erstellt, die einen enum "Color" enthaelt. Im Konstruktor dieser Klasse soll ein Argument übergeben werden, welches dann auf ein Attribut des enums zugewiesen werden soll.

    Was muss ich im Hauptprogramm dem Konstruktor uebergeben?
    Hier mal der Code:

    class Spielstein {
    public:
        virtual ~Spielstein() {}
        virtual int getColor() { return farbe; }
        enum Color {grau=0, rot,blau,gelb};
    
    protected:
        Color farbe;
    };
    
    class Stein : public Spielstein {
    public:
        Stein(???) { farbe=???;}
        virtual ~Stein() {}
    };
    

    Dies sind meine beiden Klassen.

    Und im Hauptprogramm moechte ich dann ein Objekt der Klasse Stein erstellen.
    Ungefaehr so:

    int main(int argc, char** argv) {
    
        Stein s=Stein(rot);
        cout << s.getColor() << endl;
        return 0;
    }
    

    Ich hoffe, ihr koennt mir dabei helfen 🙂



  • Spielstein::rot



  • class Stein : public Spielstein
    {
    public:
        Stein(Spielstein::Color p_color) : farbe(p_color) {}
        virtual ~Stein() {}
    };
    


  • Übrigens solltest du Funktionen nur virtuell machen, wenn du auch vorhast, sie in abgeleiteten Klassen neu zu definieren (mit Ausnahme des Destruktors, welcher für polymorphe Zerstörung notwendig ist). Hingegen reicht in der abgeleiteten Klasse der compilergenerierte Destruktor, du musst ihn nicht nochmals selbst definieren.

    Und das =0 im ersten Enumerator ist auch unnötig, da der erste Enumerator implizit immer Null ist. Abgesehen davon, dass die eigentlichen Zahlenwerte meist irrelevant sind.



  • Also wenn ich in einer Basisklasse eine Funktion definiert habe, muss ich davor nicht virtual schreiben, wenn ich diese Funktion in der Unterklasse nur benutzen möchte, wie sie in der Oberklasse definiert ist, richtig?

    Gut, hatte gedacht, man müsste immer virtual schreiben, wenn man die Funktionen quasi übernehmen möchte.
    Danke 🙂



  • virtual brauchst du, wenn du Zeiger auf Objekte einer abgeleiteten Klasse in Zeigern auf Objekte der Basisklasse speichern willst.
    Beispiel: http://codepad.org/A28Ku77O
    Sieh dir dir Ausgaben an, du wirst sehen, dass bei der letzten Ausgabe "Hello World! Hello World!" und nicht "Hallo Welt! Hallo Welt!" erscheint, weil virtual nicht verwendet wurde 😉


  • Administrator

    314159265358979 schrieb:

    virtual brauchst du, wenn du Zeiger auf Objekte einer abgeleiteten Klasse in Zeigern auf Objekte der Basisklasse speichern willst.

    Unsinn. virtual hat nichts mit Zeigern zu tun.

    #include <iostream>
    
    struct base
    {
      void foo() { std::cout << "base::foo" << std::endl; }
      virtual void bar() { std::cout << "base::bar" << std::endl; }
    };
    
    struct derived
      : base
    {
      void foo() { std::cout << "derived::foo" << std::endl; }
      void bar() { std::cout << "derived::bar" << std::endl; }
    };
    
    int main()
    {
      std::cout << "derived d" << std::endl;
      derived d;
      d.foo();
      d.bar();
    
      std::cout << "\nbase& rb = d" << std::endl;
      base& rb = d;
      rb.foo();
      rb.bar();
    
      std::cout << "\nbase b" << std::endl;
      base b;
      b.foo();
      b.bar();
    }
    
    derived d
    derived::foo
    derived::bar
    
    base& rb = d
    base::foo
    derived::bar
    
    base b
    base::foo
    base::bar
    

    Wie man sieht keine Zeiger. Bei virtual geht es nur um die Polymorphie. Bei virtual bei Funktionen geht es nur darum, welche Funktion aufgerufen werden soll. Soll die Funktion aufgerufen werden, welche zum statischen Typ der Variable gehört (siehe rb.foo() -> statischer Typ von rb ist base ) oder soll die in der Hierarchie des Objektes am spezialisiertesten Funktion zum Einsatz kommen (siehe rb.bar() -> das Objekt ist tatsächlich vom Typ derived , daher ist die spezialisierteste Funktion derived::bar ). Dabei genügt es, wenn virtual einmal in der Basisklasse hingeschrieben wurde. Meistens schreibt man aber virtual trotzdem hin, um es zu verdeutlichen, dass es sich um eine virtuelle Funktion handelt, bzw. im neuen C++ Standard kann man dazu override hinter der Funktion hinschreiben.

    Übrigens:

    base b2 = d;
    // Das ist eine Kopie!
    // b2 ist ein Objekt vom Typ base, alle Informationen über derived gehen
    // bei der Kopie verloren. Sowas nennt man auch Slicing.
    b2.foo();
    b2.bar();
    // erzeugt dasselbe Ergebnis wie beim Beispiel mit b!
    // base::foo
    // base::bar
    
    base* pb = &d;
    pb->foo()
    pb->bar();
    // verhält sich wie das Beispiel mit rb.
    // base::foo
    // derived::bar
    

    Es geht somit über Zeiger und Referenzen, da dort das Objekt nicht kopiert wird. Es ändert sich nur der statische Typ der Variable, aber nicht der tatsächliche Typ des Objektes.

    Grüssli



  • Das habe ich doch gesagt, wenn auch weniger ausführlich, abgesehen davon, dass ich Referenzen nicht erwähnt habe.


  • Administrator

    314159265358979 schrieb:

    Das habe ich doch gesagt, wenn auch weniger ausführlich, abgesehen davon, dass ich Referenzen nicht erwähnt habe.

    Nein, du hast folgendes gesagt:

    struct base
    {
      void foo() { };
    };
    
    struct derived
    {
    };
    
    int main()
    {
      derived d;
      base* p = &d;
    }
    

    Dafür bräuchte man virtual . Wie du siehst, ist das aber hinten und vorne nicht der Fall. Womöglich hast du das gemeint, was ich geschrieben habe, aber das hast du nicht hingeschrieben. Was du hingeschrieben hast stimmt einfach nicht.

    Grüssli



  • Ich sagte für das erwünschte Verhalten braucht man virtual, und das ist richtig.


  • Administrator

    314159265358979 schrieb:

    Ich sagte für das erwünschte Verhalten braucht man virtual, und das ist richtig.

    Ich will ja nicht pingelig sein, aber das ging aus deinem Beitrag hinten und vorne nicht raus. Geschrieben hast du völlig was anderes. Vergiss all dein Wissen über virtual und Polymorphie in C++ und liess dann nochmals deinen Beitrag. Würdest du als Anfänger daraus wirklich schlau werden? Ich habe so meine Zweifel 😉

    Grüssli



  • Du hast Recht, wenn du sagst, dass meine Erklärung scheiße ist. Richtig ist sie trotzdem.


  • Administrator

    Dann erklär mir mal, wieso der folgende Satz richtig sein soll:

    314159265358979 schrieb:

    virtual brauchst du, wenn du Zeiger auf Objekte einer abgeleiteten Klasse in Zeigern auf Objekte der Basisklasse speichern willst.

    Ich füge in diesem Satz ein Wort ein und dann wäre er richtig:
    virtual brauchst du nicht, wenn du Zeiger auf Objekte einer abgeleiteten Klasse in Zeigern auf Objekt der Basisklasse speichern willst.

    Es ist nicht nur scheisse erklärt, sondern der Satz stimmt so auch gar nicht.

    Grüssli



  • Dass da implizit steht "wenn du dieses Verhalten haben willst", habe ich vorrausgesetzt.
    Mir aber auch latte, erklärs ihm einfach so, wie du es für richtig hältst.



  • Also ohne die Erklärung mit den Beispielen hätte ich ehrlich gesagt deinen Satz nicht ganz verstanden 😉

    Wie dem auch sei, danke für die Erklärung zur Benutzung des virtuals, auch wenn das nicht das Hauptthema war. Beides ist jetzt aber auch erklärt und hoffentlich verstanden 🙂



  • Manchmal glaube ich es gibt hier zwei User mit dem (annähernd) gleichen Namen

    314159265358979 oder so...

    Einer schreibt teilweise echt kompetente Beiträge und einer behauptet Mist
    und versucht ihn dann krampfhaft als richtig zu verkaufen.



  • Ich habe noch einmal eine Frage bzw. ein Problem mit enums.
    Ich habe in einer Klasse ueber dem Konstruktor ein enum definiert, welches "Position" heißt. Die Klasse besitzt ein Attribut mit dem Typ dieses enums.

    Wenn ich aber in einer Methode der Klasse auf dieses Attribut zugreifen will bzw. es setzen möchte, kommt ein Fehler.

    Hier mal die Klasse: (nur das Nötige)

    class Spielbrett {
    
    public:
        enum Position { normal=0, links=90, kopf=180, rechts=270 };
    
        Spielbrett() { 
          // Initialisiere das Spielbrett
          init(); 
        }
        ~Spielbrett() {
        } // destructor
    
        void init();
    
    private:
    
        Position pos;
    };
    

    Und hier ist die .cpp-Datei mit einer Methode:

    #include "Spielbrett.h"
    #include <iostream>
    
    using namespace std;
    
    void Spielbrett::init() {
    /* Zeile 26 */    pos=Position::normal;
    }
    

    Fehler ist:

    Spielbrett.cpp: In member function 'void Spielbrett::init()':
    Spielbrett.cpp:26: error: 'Position' is not a class or namespace
    

    Dabei ist doch Position ein enum der Klasse. Was ist falsch?



  • pos = normal;
    


  • Ach, ich bin mal wieder auf den Kopf gefallen! D.h., wenn ich auf ein enum zugreifen will, muss ich davor nicht den enum-typen schreiben. Da hat mich wohl etwas anderes durcheinander gebracht.

    Danke sehr!



  • In C++0x gibt es enum class bzw. enum struct , mit denen du das musst.

    Vorher verwendet man oft so ein Idiom, um qualifiziert auf die Enums zugreifen zu können:

    namespace Position
    {
        enum Type
        {
            // Enumeratoren hier
        };
    }
    

Anmelden zum Antworten