Frage zu Oberklasse und Methoden in Unterklassen



  • Benja_m schrieb:

    Ich möchte, dass alle Klassen, die von "ocl" abgeleitet sind, eine Methode

    int read(const int param)
    

    zur Verfügung stellen müssen. Diese soll aber jeweils unterschiedlich arbeiten (bei gleichen Parametern).

    Das hört sich nach einer rein virtuellen Funktion an:

    class ocl {
      ...
      virtual int read(int param) = 0; // const-Wertparameter machen nicht wirklich viel Sinn, schon gar nicht in einer Deklaration
    };
    

    Wenn du Java kennst, das ist das gleiche wie eine abstrakte Methode in Java.

    Ich habe eine map in der sowohl Klassen "uclass1" als auch "uclass2" abgelegt werden sollen. Und nachdem in der map das richtige Element gefunden wurde, soll dann obige Funktion genutzt werden. Es soll aber entsprechend die richtige genutzt werden. Wenn das Objekt in der Map also vom Typ uclass1 ist, dann soll uclass1.read() genutzt werden, wenn der Tyo uclass2 ist, dann soll uclass2.read() genutzt werden.

    Das ist Polymorphie (ermöglicht durch das Schlüsselwort virtual). Dazu musst du Zeiger auf ocl in die map packen, dann funktioniert das so, wie du dir das vorstellst:

    map<int, ocl*> myMap;
    map<int, ocl*>::iterator it;
    // map füllen ...
    it->second->read(egal);
    // nicht vergessen, mapeinträge ggf. wieder zu deleten
    


  • hehe schneller 🙂



  • 1. pur virtuelle Methoden:

    class ocl
    {
    public:
      virtual int read(int param) = 0;
      //btw, "const int" ist Unsinn ;)
    };
    
    class ucl1 : public ocl
    {
    public:
      int read(int param) {...}
    };
    
    class ucl2 : public ocl
    {
    public:
      int read(int param) {...}
    };
    

    2. Speichere keine Objekte in der Map, sondern Pointer - dann klappt die Bestimmung der "richtigen" read()-Methode dank virtueller Methoden (und 1) von alleine.

    map<int ocl*> myMap;
    map<int, ocl*>::iterator pos=myMap.find(4711);
    if(pos!=myMap.end()) return pos->second->read(0xAFFE);
    


  • Hi

    Danke euch !

    Werd ich testen und dann Rückmeldung geben.

    Bzgl. der const int - da hat mir mal jemand gesagt, so umgeht man das Problem, dass man ausversehen in die übergebenen Parameter etwas reinschreibt. Klang logisch, hab ich dann übernommen.

    Benjamin



  • Benja_m schrieb:

    Bzgl. der const int - da hat mir mal jemand gesagt, so umgeht man das Problem, dass man ausversehen in die übergebenen Parameter etwas reinschreibt. Klang logisch, hab ich dann übernommen.

    Das Problem bekommst du aber nur, wenn du die Parameter per Referenz übergibst - "int" wird als Kopie übergeben, hat also keine Rückwirkung auf den Aufrufer, "int&" wird als Referenz übergeben, ändert also im Ernstfall den übergebenen Parameter.

    (btw, Übergabe per const Referenz macht eher bei User-Klassen Sinn als bei eingebauten Typen wie int)



  • soo, ich hab da zwar was, aber es klappt nicht. 🙂
    Ich hole hier mal weiter aus um genau aufzuzeigen, was ich mache und möglicherweise die fehlersuche zu erleichtern.
    sage dazu, ich arbeite unter linux mit eclipse und cdt und dem gcc 3.3.3

    hier mal die Ausschnitte.

    ocl.h (da werden FUnktionen und Variablen deklariert, die allen Unterklassen gemein sein sollen. Hier nur AUsschnitte)

    ...
    class ocl {
    
    public:
        ocl();
        virtual ~ocl();
    
        void setName(string name);
        virtual int read(int par) = 0;
        virtual string getName() = 0;
    
    protected:
        string name;
    ....
    };
    

    ocl.cpp

    ocl::ocl() {
        name = "hallo";
    }
    
    ocl::~ocl() {
    }
    
    ocl::setName(string newName) {
        name = newName;
    }
    ...
    

    uclass1.h (hier jetzt eine der abgeleiteten Unterklassen)

    class uclass1 : public ocl {
    public:
        uclass1();
        virtual ~uclass1();
        int read(int par);
        string getName();
    };
    

    uclass1.cpp

    uclass1::uclass1() {
        // tut einiges
    }
    
    uclass1::~uclass1() {
    }
    
    int uclass1::read(int par) {
        //tut was mit par und gibt dann was zurück
    }
    
    string uclass1::getName() {
        return name;
    }
    

    soo, und nun hab ich in einer anderen Klasse "control".
    control.h

    class control {
    public:
        addUclass1();
    private:
        map<int, ocl*> myMap;
    };
    
    control::control
    
    control::addUclass1() {
        uclass1 ucl;
        ucl.setName("Ich bin doof");
        uclass1* pucl;
        pucl = ucl;
    
        myMap.insert(make_pair(100, pucl));
    }
    
    control::doIt() {
        map<int, ocl*>::iterator it;
        it = myMap.begin();
        cout << "name is = " << it->second->getName() << endl;
    }
    

    Wenn ich nun in control erst addUclass1() aufrufe, und danach doIt(),
    dann kommt mein Programm bis zur Zeile vor dem cout.
    Aber dann bricht mein Programm einfach ab. Keine Fehlermeldung, kein garnix. Es passiert einfach nichts mehr.
    Der Aufruf

    it->second->getName();
    

    muss irgendetwas schlimmes sein.
    Ein

    it->second->read(73);
    

    bewirkt identisches 😞
    Ich sehe nur nicht, wieso und was dort passiert.
    Ich habe das jetzt stark gekürzt/vereinfacht. Ich hoffe, mein Problem wird deutlich und ich habe keine wichtigen Sachen vergessen dazuzusagen.
    Wenn doch bitte fragen !

    Danke euch !



  • sorry, Tippfehler oben:

    control::addUclass1() {
        uclass1 ucl;
        ucl.setName("Ich bin doof");
        uclass1* pucl;
        pucl = & ucl;
    
        myMap.insert(make_pair(100, pucl));
    }
    

    muss mich mal registrieren um editieren zu können 🙂



  • Maxi schrieb:

    class ocl
    {
       virtual void read() = 0;
    };
    

    das bedeutet, dass read nicht implementiert ist und erst in den unterklassen implementiert wird.

    Das ist so nicht ganz richtig. Eine Implementation kann da sein - und muss z.B. auch im Falle eine pure virtual Destruktors. Man wird auf jeden Fall zum Überschreiben der Methode gezwungen.



  • addUclass1() kann so nicht funktionieren. Du speicherst in deiner map einen Pointer auf ein lokales Objekt welches nach Verlassen der Funktion nicht mehr gültig ist. Du mußt den Pointer schon dynamisch erstellen (mit new) und zum Schluß natürlich auch wieder löschen (im Destruktor von control).



  • @Benja: Du kannst keine Pointer auf lokale Variablen in die map packen (OK, du kannst schon, solltest aber nicht). Die Variable 'ucl' wird am Ende der Funktion addUclass1() wieder gelöscht und der Pointer, den du in die map gelegt hast, zeigt anschließend ins Nirvana. Besser ist es, die Variable per new auf dem Heap anzulegen (und im Destruktor deiner control-Klasse für alle Map-Einträge delete aufzurufen).



  • 7H3 N4C3R schrieb:

    Maxi schrieb:

    class ocl
    {
       virtual void read() = 0;
    };
    

    das bedeutet, dass read nicht implementiert ist und erst in den unterklassen implementiert wird.

    Das ist so nicht ganz richtig. Eine Implementation kann da sein - und muss z.B. auch im Falle eine pure virtual Destruktors. Man wird auf jeden Fall zum Überschreiben der Methode gezwungen.

    auch wenns offtopic ist: welcher teufel kann einen Menschen dazu bewegen, dass er einen pure virtual dtor benutzt?



  • Benja_m schrieb:

    soo, und nun hab ich in einer anderen Klasse "control".
    control.h

    class control {
    public:
        addUclass1();
    private:
        map<int, ocl*> myMap;
    };
    
    control::control
    
    control::addUclass1() {
        uclass1 ucl;
        ucl.setName("Ich bin doof");
        uclass1* pucl;
        pucl = ucl;
        
        myMap.insert(make_pair(100, pucl));
    }
    

    zu control::addUclass1: Ich wundere mich, dass das compiliert wird. Sicher, dass da nicht steht pucl = **&**ucl; ? Nunja, in jedem Falle fügst du damit einen Zeiger auf ein lokales Objekt in die Map ein, und wie du sicher weißt, werden lokale Objekte zerstört, sobald ihr Gültigkeitsbereit (in dem Falle also die Funktion addUclass1) verlassen wird. In der Map steht somit ein Pointer, der auf irgendwas unbestimmtes zeigt.

    Die Lösung besteht darin, Objekte mit dynamischer Lebensdauer in die Map einzufügen:

    void control::addUclass1() {
        uclass1* ucl = new uclass1();
        ucl->setName("Ich bin doof");
        myMap.insert(make_pair(100, ucl));
    }
    

    irgendwann müssen diese Objekte auch wieder zerstört werden, diesmal manuell:

    for (map<int,ocl*>::const_iterator it = myMap.begin(); it != myMap.end(); ++it)
      delete it->second;
    


  • otze schrieb:

    auch wenns offtopic ist: welcher teufel kann einen Menschen dazu bewegen, dass er einen pure virtual dtor benutzt?

    Dafür könnte man einen beliebigen, konstruierten Grund einsetzen. 🤡 Ob man abstrakten Methoden eine Implementation verpasst oder nicht ist sicherlich diskuissionswürdig - aber grundsätzlich geht es und ist erlaubt.



  • @all:
    Manchmal sieht man den Wald vor lauter Bäumen nicht.
    Lokale Variable... Lernt man eigentlich ziemlich früh beim Programmieren ...
    Und ich habs nichtmal beim Abtippen/Abstrahieren gemerkt 😞
    Herzlichen Dank, jetzt klappt es.

    @Bashar:
    siehe http://www.c-plusplus.net/forum/viewtopic-var-p-is-982918.html#982918
    Natürlich hatte ich das & vergessen...


Anmelden zum Antworten