Objekte dynamisch erzeugen, in (z. B.) Vector schreiben, auf die Objekte zugreifen



  • Hallo zusammen 🙂

    Ich schreibe gerade ein Tool, der mir aus einer Datei die Zeilen parst, und je nach ID Objekte verschiedener Klassen erzeugt. Ersteres ist schon fertig, die Objekte will ich mit

    class Detlef: public Mensch
    {
      public:
        // Detlef-Konstruktor
        Detlef(int Anzahl_Finger);
    };
    

    und dann in einem Vektor speichern:

    int main()
    {
      vector<Mensch*> Leute;
      Leute.push_back(new Detlef(7));
    

    Nun, ich will auf die Objekte zu einem späteren Zeitpunkt auch anhand der Objektattribute zugreifen können, wofür sich der Vektor aber nicht eignet. Ich kann auch schlecht irgendwie sonst auf dem Objekt zugreifen, da ich ihm keinen spezifischen Namen vergeben habe. (Am obigen Beispiel: ich möchte in dem Vector aller Menschen genau den Detlef finden, der 7 Finger hat, um ihm zu Weihnachten 7-Finger-Handschuhe zu schenken)

    Was mir dazu einfällt, wären Zeiger auf die Objekte und eventuell ein mapping um diese anhand dem eindeutigen Schlüssel zu finden (bei mir kann es nur je einen Detlef mit 1, 2, .. 12 Fingern geben). Das hab ich aber noch nicht zu Ende ausgedacht, und Zeiger sind schon nicht mein Lieblingswerkzeug. Vielleicht gibt es eine andere Struktur die Zugriff auf die Elemente über Attribute einfacher machen könnte?

    Wenn jemand so was ähnliches schon mal gemacht hat oder einen Vorschlag hat, bitte mit etwas mehr als einem Wort erklären, am besten mit kleinem Code-Beispiel 🙂

    LG,
    Cordula



  • Hallo,

    Nun, ich will auf die Objekte zu einem späteren Zeitpunkt auch anhand der Objektattribute zugreifen können, wofür sich der Vektor aber nicht eignet.

    Warum sollte sich der vector dafür nicht eignen?

    und Zeiger sind schon nicht mein Lieblingswerkzeug

    Warum nimmst du dann hier Zeiger? Warum nicht einfach sowas?

    vector<Mensch> Leute;
    

    Zum Suchen gibt es z.Bsp. std::find_if. Dem kann man einen Funktor mitgeben in dem man beliebige Suchkriterien implementieren kann.



  • Gib erstmal ein sinnvolleres Beispiel. Detlef ist ein Exemplar vom Typ Mensch mit dem Wert "Detlef" für seinen Namen. 0..n Finger haben alle Menschen. Eine einigermaßen sinnvolle Spezialisierung von Mensch wäre Mann/Frau, wobei man das eigentlich auch eher nicht durch Vererbung machen würde. Wenn du dann eine Dame mit 12 Fingern suchst benutzt du std:find_if mit einem Predicate wie

    struct GenderAndFinger{
      Gender gender_;
      unsigned finger_;
      bool operator()(const Mensch& l, const Mensch& r) const;
    };
    

    Deine Aufgabe klingt allerdings eher so, dass Du über einen Basisklassenzeiger Instanzen eine bestimmten Spezialisierung suchst. Sowas riecht direkt immer nach einem nicht besonders gut durchdachten Design. Entweder hast du eine Generalisierng oder eben nicht. Nichtsdestotrotz gibt es hier auch Lösungen (wobei erstmal eher grundlegend über das Design nachgedacht werden sollte). Das hässlichste wäre in einem find_if per dynamic_cast zu überprüfen, ob es sich um den gewünschten Typ handelt 👎. Eine andere Möglichkeit ist das Visitor Pattern, auch nciht gerade 👍. Eine bessere Lösung wäre nicht nach dem Typ zu suchen, sondern nach einer Eigenschaft wie BrauchtHandschuhe() , die man in die Schnittstelle der Basisklasse aufnimmt, wenn sie dort sinn macht.



  • Hallo 🙂

    Danke schonmal.

    @Braunstein: Weil auf Vector-Elemente direkt nur über ihre Position zugegriffen werden kann. Über find_if müßte ich erstmal lesen .. 🙂
    Geht das

    vector<Mensch> Leute;

    überhaupt? Ich will ja ein Vector mit Elementen vom Typ Männern und Frauen (Unterklassen von Mensch) haben?

    @brotbernd: Ein anderes Beispiel:

    Ich nehme aus meinem File die Zeilen

    Mann 12Finger 184cm brauneHaare
    Mann 08Finger 192cm wenigHaare
    Frau 10Finger 175cm blondeHaare

    Davon erstelle ich 2 Objekte von Mann und eins von Frau durch

    MenschenVec.push_back(new Mann(12Finger, 184cm, brauneHaare));
    MenschenVec.push_back(new Mann(08Finger, 192cm, wenigHaare));
    MenschenVec.push_back(new Mann(10Finger, 175cm, blondeHaare));
    

    Und jetzt war mein Gedanke, gleichzeitig ein map anzulegen:

    MenschenVec.push_back(new Mann(12Finger, 184cm, brauneHaare));
    MenschMap.insert(pair<string,Mensch*>(Zeile.substr(5, 13), MenschenVec[MenschenVec.size()-1]));
    // Key: Zeile.substr(5, 13)= Mann
    // Value: MenschenVec[MenschenVec.size()-1]) = aktuell letztes Objekt im Vector
    

    Dann könnte ich später einfach in dem Map über "10Finger" suchen und auf das jeweilige Element im Vector zugreifen:

    MenschMap.find("10Finger")->second
    

    Eine andere Frage wäre, wie ich von hier eine Eigenschaft der Frau mit den 10 Fingern ändern könnte (z. B. den Attribut Haare).

    Wäre der Weg sehr häßlich? Abgesehen davon, dass es über find_if einfacher gehen würde?

    P.S. Ach, übrigens: ich meinte, dass die Anzahl der Finger eindeutig eine Person identifiziert.



  • Wenn du Polymorphie plus vector nutzen willst mußt du natürlich Zeiger nehmen (oder Smartpointer).

    Wieso sind bei dir Größe und Fingerzahl Strings? Das sollten doch eher Zahlen sein.
    Der weg über die map ist hässlich. Du müsstest ja auch beide Listen synchron halten (bei zufüfgen und löschen). Wenn du dann nach anderen Dingen als Fingern suchen willst bräuchtest du weitere maps. Ich denke du siehst wohin das führt.



  • Weil auf Vector-Elemente direkt nur über ihre Position zugegriffen werden kann.

    Na und?

    Was unterscheidet denn Männer und Frauen in deinem Modell derart, dass du dafür zwei verschiedene Klassen brauchst?



  • Hallo Cordula,

    mmmh... so nebenher, bei dem was Du vorhast, würde ich von Maps (so wie Du sie verwendest) echt abraten. Bei einer std::map<K,T> muss jeder Key eindeutig sein. Das würde bedeuten, dass Du nur z.B. eine einzige Person mit z.B. braunen Haaren oder zehn Fingern ansprechen kannst.

    Gruß
    PuerNoctis



  • @PuerNoctis: Die Elemente sind in der Tat genau einmal vorhanden, bzw. der Schlüssel nach dem ich nach den Objekten suchen würde, hier die Fingeranzahl, ist eindeutig. Gut, die Fingeranzahl als Beispiel zu nehmen war vielleicht unglücklich. Ist das der einzige Grund das Map nicht zu benutzen?

    @Michael E.: Und wenn ich die Objekte nicht in einem anderen Container halte, wo ich sie über den Key erreichen kann, nützt mir die Position im Vector wenig. Außer man nimmt das find_if, aber ich weiß nicht ob das nicht etwas übertrieben ist für die konkrete Anwendung.
    Hier braucht man keine gesonderten Klassen, mein Program soll biochemische Zusammenhänge verarbeiten und ich wollte nicht den konkreten Code als Beispiel nennen, da er 4mal mehr Erklärungen bedarf als die Menschen mit den Fingern bedarf.



  • Wenn es tatsächlich um Männer und Frauen geht, spar dir die Vererberei und speicher Mann oder Frau in der Klasse Mensch. Es wird (vermutlich) nie etwas anderes geben als Männer und Frauen, du brauchst hier also keine erweiterbare Lösung. Das eventuell extrem unterschiedliche Verhalten von Mann und Frau kannst Du z.B. mit dem Strategie Muster modellieren. Da kann man dann auch später noch das Verhalten einer zickigen Frau dazudichten.

    Deine Sucherei:

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    
    class Mensch
    {
    public:
        enum Gender {Frau, Mann};
    
        Mensch(const std::string& name, Gender g, unsigned groesse) :
            geschlecht_(g), groesse_(groesse), finger_(10), name_(name)
        {}
    
        Gender Geschlecht() const{ return geschlecht_; }
        unsigned Finger() const { return finger_; }
        const std::string& Name() const { return name_; }
    
        void HackFingerAb(unsigned finger)
        {
            finger_ -= std::min(finger_, finger);
        }
    
    private:
        Gender geschlecht_ ;
        unsigned groesse_, finger_;
        std::string name_;
    };
    
    bool FrauMit7Fingern(const Mensch& m)
    {
        return m.Geschlecht() == Mensch::Frau && m.Finger() == 7;
    }
    
    int main ()
    {
        std::vector<Mensch> menschen;
        menschen.push_back(Mensch("Uschi", Mensch::Frau, 175));
        menschen.push_back(Mensch("Uwe", Mensch::Mann, 125));
        menschen.push_back(Mensch("Chantalle", Mensch::Mann, 185));
    
        menschen[0].HackFingerAb(3); // Hack Uschi 3 Finger ab!
    
        std::vector<Mensch>::const_iterator it = 
            std::find_if(menschen.begin(), menschen.end(), FrauMit7Fingern);
    
        std::cout << it->Name(); // Uschi
        return 0;
    }
    


  • Cordula schrieb:

    @Michael E.: Und wenn ich die Objekte nicht in einem anderen Container halte, wo ich sie über den Key erreichen kann, nützt mir die Position im Vector wenig. Außer man nimmt das find_if, aber ich weiß nicht ob das nicht etwas übertrieben ist für die konkrete Anwendung.

    Es tut mir Leid, aber ich verstehe dein Problem nicht.



  • @brotbernd: Wow! Vielen Dank für die Mühe die du dir gemacht hast 🙂 Ich werde schon Klassen nehmen, werde also dein Code anpassen 🙂

    Nur so zur Info - ist das mit dem map sehr schlecht, abgesehen davon dass es viel uneffektiver ist als find_if? Ich will was lernen 🙂 Danke nochmal.



  • find_if ist eine simple Schleife die alle Elemente im vector anguckt, bis was passendes gefunden ist. Es kommt drauf an, was du mit effizient meinst. Das Suchen geht in der map viel schneller. Dafür dauert das Einfügen länger und mehr Speicher wird benötigt. Wenn Du eine Datei einliest und danach keine Elemente mehr hinzukommen, wäre ein sortierter Vector wohl am effizientesten.
    Wenn du von Programmierfiizienz redest, ist die einfachste Lösung die beste und das ist wohl vector + find_if.



  • brotbernd schrieb:

    menschen.push_back(Mensch("Chantalle", Mensch::Mann, 185));
    

    Das sind doch die, die in Nachtbars als "Bardamen" arbeiten, oder?

    Edit: Sorry, offtopic



  • Danke für die ausführliche Erklärung 🙂

    Den Code hab ich auch schon _fast_ angepasst 🙄

    Einen Fehler werd ich nicht los ..

    invalid use of non-static member function 'bool Read::Fraumit7Fingern(const Mensch&)'

    Jemand ne Idee?



  • Du rufst die Funktion ohne Instanz auf:

    Mensch foo;
    
    // du machst:
    Read::Fraumit7Fingern(foo);
    
    // richtig wäre, da es sich nicht um eine statische Funktion handelt:
    Read reader;
    reader.Fraumit7Fingern(foo);
    

    Das ist aber nur ne Vermutung, weil du keinen Quelltext zeigst.



  • Danke, das hat gestimmt.

    Aber danach bekomm ich trotzdem andere Fehler zurück. Hier der Code:

    vector<Mensch> MenschVec;
    vector<Mensch>::iterator itMe;
    
    MenschVec.push_back(Mensch(attr1, attr2, attr3));
    MenschVec.push_back(Mensch(attr4, attr5, attr6));
    MenschVec.push_back(Mensch(attr7, attr8, attr9));
    
    std::vector<Mensch>::const_iterator itMe = std::find_if(MenschVec.begin(), MenschVec.end(), FrauMit7Fingern);
    cout << itMe -> Haare << endl;
    

    Also soll die Frau mit den 7 Fingern gefunden werden und ihre Haarfarbe zurückgegeben werden.
    Nach der Korrektur heißt es:

    'itMe' has previous declaration as '__gnu_cxx::__normal_iterator<Mensch*,std::vector<Mensch,std::allocator<Mensch>>>'
    No matching function for call to 'Read::Fraumit7Fingern'
    Conflicting declaration '__gnu_cxx::__normal_iterator<const Mensch*std::vector<MEnsch,std::allocator<Mensch>>>itMe'



  • Zum 1. und 3. Fehler: du hast itMe 2mal deklariert. Lösch einfach vector<Mensch>::iterator itMe;

    zum 2. Fehler: Dein Compiler findet Read::Fraumit7Fingern' nicht.
    Dazu kann man wegen fehlenden Code nichts sagen.



  • Ich möchte nicht übertreiben - aber ich bin grad schon übermüdet, also eine letzte Frage:

    Was heißt

    argument of type 'bool (Read:: )(const Mensch&)' does not match 'bool (Read::*)(const Mensch&)'

    Ich hab eine Erklärung über calling a member function using a pointer-to-member function gefunden, aber ehrlich gesagt hab ich sie nicht wirklich verstanden. Und da ich damit keine Erfahrung habe ist es noch schwieriger 🙄

    Danke für eure Antworten.



  • Scheinbar rufst du FrauMit7Fingern nicht korrekt auf.
    Warum muß diese Funktion denn eine Memberfunktion sein?

    Ich bevorzuge an der Stelle eigentlich Funktoren. Also etwa so:

    struct FrauMitAnzahlFingern {
     private:
      int m_anzahl;
     public:
      FrauMitAnzahlFingern(int anzahl) : m_anzahl(anzahl) {}
      bool operator()(const Mensch& mensch) {
        return mensch.Geschlecht() == Mensch::Frau && mensch.Finger() == m_anzahl;
      }
    };
    
    // Aufruf dann so
    std::vector<Mensch>::const_iterator itMe = std::find_if(MenschVec.begin(), MenschVec.end(), FrauMitAnzahlFingern(7));
    

    Da kann man dann auch gleich nach anderen Anzahlen suchen. Außerdem hat es der Compiler etwas leichter mit dem Optimieren.



  • Läuft 🙂

    Find ich auch übersichtlicher mit dem Funktor 🙂

    Danke vielmals, und noch nen schönen Abend.
    C.


Anmelden zum Antworten