Problem bei Zuweisung von Werten in map STL



  • Hallo Leute,
    Ich hab das Forum schon eine Zeit lang als stiller Beobachter mitverfolgt und mich vorwiegend aufgrund eines konkreten Problems, auf das ich heute gestoßen bin mal registriert.

    Ich stehe derzeit vor der Aufgabe ein Prototype Pattern in C++ zu implementieren.
    Ich habe dieses bereits in Java erfolgreich implementiert und bin dabei auch auf keine weiteren Probleme gestoßen, was ich unter C++ leider nicht behaupten kann..

    So um nun auf den Punkt zu kommen, ich hänge derzeit bei der Verwendung einer map aus der STL.
    Um kurz überblicksmäßig zu erklären, wozu ich eine map brauche:
    Ich habe eine FactoryKlasse, welche eine map von allen Möglichen Objekten beinhaltet, die im Nachhinein geklont werden können.
    Der map Typ eignet sich dafür gut, da ich als Index den Namen des Objekts verwenden kann und es somit mit geringen Aufwand innerhalb der map finden kann.
    Die map soll erstmal als leer instanziert werden und im Nachhinein soll es möglich sein neue Objekte hinzuzufügen oder bestehen Objekte aus der map zu clonen.

    Soweit sogut.
    Meine Factory Klasse beinhaltet nun eine map, die ich auf zwei Arten ausprogrammiert habe.
    Möglichkeit 1:
    .h

    class WeaponFactory {
    public:
           static std::map<std::string, Weapon*> initMap(void);
    private:
            static std::map<std::string, Weapon*> _objectPool;
    };
    

    .cpp

    std::map<std::string, Weapon*> WeaponFactory::_objectPool = WaeponFactory::initMap();
    
    std::map<std::string, Weapon*> WeaponFactpry::initMap() {
         std::map<std::string, Weapon*> m;
         return m;
    }
    

    Möglichkeit 2:
    .h

    class WeaponFactory {
    private:
            static std::map<std::string, Weapon*>* _objectPool;
    };
    

    .cpp

    std::map<std::string, Weapon*>* WeaponFactory::_objecPool = new std::map<std::string, Weapon*>();
    

    Das eigentliche Problem ist jetzt bei der Funktion zum Hinzufügen von Objekten enstanden. Zu diesem Zeitpunkt ist die Map in beiden fällen ja noch leer.
    Hierbei wollte ich folgenden Herangehensweise verwenden:

    /*@param name: Name des hinzuzufügendem Objekt (zb.: Schwert)
      @param w: Pointer zum Objekt selbst
    */
    void WeaponFactory::addWeapon(std::string name, Weapon* w) {
        _objectPool[name] = w;
    }
    

    Hierbei bekomme ich nun folgenden Fehler:
    " error C2784: "bool std::operator <(const std::_Tree<_Traits> &,const std::_Tree<_Traits> &)": template-Argument für "const std::_Tree<_Traits> &" konnte nicht von "const std::string" hergeleitet werden. "
    Ich habe bereits etwas recherchiert und einige Beispiele gefunden, wo auf eine map auf genau die Art und Weise zugegriffen wird.
    Was mache ich falsch? Werde aus der Meldung leider nicht schlau 😞

    Danke im Vorhinein



  • omg..
    Ich bin jetzt ca 5h an dem Problem gesessen und kurz nachdem ich diesen Post verfasst habe, sehe ich das ich #include <string> vergessen habe -.-

    Nunja es werden wohl noch weitere Probleme folgen, also nütze ich den Thread mal für weiteres ^^

    Dennoch noch eine Frage zu obigem Code:
    Macht es in meinem bisherigen Code performance mäßig einen Unterschied ob ich einen Pointer auf die Map oder die Map selbst instanziere? Vorallem in Bezug auf dessen, dass sie als static definiert ist?



  • Die map mit new zu erzeugen macht überhaupt keinen Sinn. Weder aus Performance Gründen noch stylistisch noch irgendetwas.

    std::map<std::string, Weapon*> WeaponFactory::objectPool = WaeponFactory::initMap();
    
    std::map<std::string, Weapon*> WeaponFactory::initMap() {
         std::map<std::string, Weapon*> m;
         // Steht hier denn was?
         return m;
    }
    

    Ist schon ok, wenn die map denn static sein muss (Wieso muss sie das?) und in initMap auch irgendetwas sinnvolles passiert. Sonst reicht auch einfach

    std::map<std::string, Weapon*> WeaponFactory::objectPool;
    

    Namen die mit einem Unterstrich beginnen solltest du in C++ vermeiden. Es gibt bestimmte Kombinationn mit Unterstrich / Großbuchstaben / Doppeluznterstrich die für die Implementierung reserviert sind. In C++ macht man den Unterstrich gerne nach hinten.



  • Paxi schrieb:

    omg..
    Ich bin jetzt ca 5h an dem Problem gesessen und kurz nachdem ich diesen Post verfasst habe, sehe ich das ich #include <string> vergessen habe -.-

    passiert 😉 🙂

    Paxi schrieb:

    Dennoch noch eine Frage zu obigem Code:
    Macht es in meinem bisherigen Code performance mäßig einen Unterschied ob ich einen Pointer auf die Map oder die Map selbst instanziere? Vorallem in Bezug auf dessen, dass sie als static definiert ist?

    wenn du eine normale instanz erzeugst hast du den stress von wegen pointerarithmetik nicht (ok ist nicht wirklich stress), du hast aber vor allem nicht das problem wann du die map löschen musst
    static objekte existieren ja vom ersten aufruf bis zum programm ende,du musst dann halt wissen wann du den schlussstrich ziehst

    da werfe ich einfach mal smart pointer in den raum: boost::shared_ptr, std::auto_ptr und so weiter

    aber peformance mäßig geben die sich nichts, und selbst wenn: die 5 nanosekunden sind nicht ausschlaggeben für deinen programmfluss, da es ja eh nur einmal passiert.
    merke:

    90 Prozent der Laufzeit passieren in weniger als 10 Prozent des Programmcodes
    


  • Danke schonmal für die Antworten.

    Die Funktion initMap(), hat die map vorher noch mit ein paar Werten initialisiert, das habe ich allerdings wieder verworfen, somit kann ich mir die Funktion dann allgemein sparen, stimmt.
    Die Idee den Speicher für die map mittels new im Freestore zu allokieren kam mir so in den Sinn weil ich es von C gewöhnt bin, dass es meistens effizienter ist, soviel wie möglich mit Pointer zu arbeiten.
    In diesem Fall, da die map ja auch noch static ist, macht es aber wohl wirklich keinen Sinn.

    Mit SmartPointer habe ich mich bisher leider noch nicht beschftigt, wird aber aufjedenfall ein Thema sein, in das ich mich demnächst einlesen werde.

    Eine kleine Verständnissfrage zu Call by value/refernce hätte ich allerdings noch, hat zwar jetzte nichts direkt mit dem Threadthema zu tun aber ich nützen den Thread trotzdem mal dafür.
    Hier mal ein kleines Beispielprogramm

    #include <stdio.h>
    #include <iostream>
    
    struct mystruct {
    	int a;
    	int b;
    	int c;
    };
    
    void test(mystruct);
    void test2(mystruct*);
    void test3(mystruct&);
    
    int main() {
    
    	mystruct* s = new mystruct();
    
    	s->a = 7;
    	s->b = 3;
    	s->c = 12;
    
    	std::cout << s->a << " " << s->b << " " << s->c << " " << std::endl;
    	test(*s);
    	std::cout << s->a << " " << s->b << " " << s->c << " " << std::endl;
    
    	test2(s);
    	std::cout << s->a << " " << s->b << " " << s->c << " " << std::endl;
    
    	test3(*s);
    	std::cout << s->a << " " << s->b << " " << s->c << " " << std::endl;
    
    	while(1);
    	return 0;
    }
    
    void test(mystruct s) {
    	s.a = 2;
    }
    
    void test2(mystruct* s) {
    	s->a = 8;
    }
    
    void test3(mystruct& s) {
    	s.a = 1;
    }
    

    Im falle von test() wird s ja als Call by Value übergeben. Somit dereferenziere ich den Pointer um den Inhalt an die Funktion zu übergeben.
    Dieser wird dann in die lokale Variable s der Funktion kopiert und nur innerhalb der Funktion verändert und danach wieder zerstört -> Somit gibt es mal keine Änderung der Struktur in der main Funktion.

    Im Falle von test2() handelt es sich ja auch um Call by Value, nur das ich diesmal nicht den Inhalt selbst übergebe, sondern den Pointer auf die Struktur selbst. Der lokale kopierte Pointer wird zwar hierbei auch nach Beendigung der Funktion wieder zerstört, verändert allerdings vorher noch den Inhalt von s, da er ja auf die Adresse der Struktur selbst zeitg.

    Im Falle von test3() handelt es sich nun um Call by Reference. Sofern ich das Prinziep richig was verstanden habe wird hierbei eine Referenz auf die Struktur s übergeben, was der Adresse gleich kommen sollte oder?
    Allerdings muss ich die Funktion auch hier mit mit *s als Übergabeparameter aufrufen, was ja eigentlich wieder wie im Falle von test() der Inhalt selbst und nicht die Adresse (die Referenz) auf den Inhalt ist 😕



  • Eine Referenz ist nur ein Name für eine Variable. Du sagst hier "Übergib hier die Variable und nenne sie anschließend 's'".



  • Achso oke, das erklärt natürlich einiges..

    Das heißt aber, dass das & in diesem Falle nicht mit Adressoperator & gleichzusetzten ist oder?



  • Richtig 😉


Log in to reply