Objekte als Parameter und in multimaps



  • Erstmal danke für die Antwort, das ist das, wonach ich suche.

    Ich habe aber noch drei Fragen, denn erstens wüsste ich gern, was folgende Zeile in deinem Code machen soll:

    typedef Map::value_type MapType;
    

    Edit: Habs selber rausgefunden..

    Dann habe ich noch das Problem, daß ich nicht verstehe, was ich machen soll, wenn ich ein Objekt auf einen anderen Key "umkopieren" will.
    Muss ich mir dazu das Objekt aus der Multimap holen und dann an der neuen Stelle ein neues Objekt (also mit den Werten des alten) mit new() anlegen?

    Und schlussendlich, kannst du das mit dem deleten wenigstens etwas genauer erklären? In C++ kann man ein Objekt ja über einen Destruktor oder über delete() löschen, was davon muss ich in diesem Fall nehmen?

    Leider sind meine C++ Fähigkeiten ein wenig begrenzt, und Lektüre über Vererbung und virtuelle Methoden hat mir keine Erleuchtung bei dieser Frage gebracht.

    Achja und nochwas:
    Sind Hash-Multimaps besser?
    Und wenn ja, sind die in der STL drin und können ohne weiteres benutzt werden? In der Doku zur STL stand was davon, daß die immernoch nicht im C++ Standart drin sind und man selber eine Hashfunktion schreiben müsste, was für mich als Anfänger dann doch eher schlecht wäre...



  • Hi

    zu 1.
    Hast du ja anscheinend jetzt verstanden:
    der Typedef dient nur dazu, nicht so viel tippen zu müssen...

    zu 2.
    Wenn du ein Objekt kopieren willst, dann mußt du das neue Objekt wieder mit new erzeugen (am besten dafür dann einen Copy-Konstruktor verwenden).
    Wenn du ein Objekt einfach an eine andere Koordinate verschieben willst, dann brauchst du das Objekt nicht nochmal neu erzeugen (da es ja schon existiert).
    Du mußt es nur an der alten Koordinate löschen (s. 3) und an der neuen Koordinate einfügen.

    zu 3.
    wenn ein Objekt mit 'new'erzeugt wurde, muß es auch irgendwo wieder mit 'delete' gelöscht werden (dieser ruft dann implizit den Destruktor der Klasse auf).
    Wenn du ein Objekt aus der Multimap komplett löschen willst, dann mußt du es nicht nur aus der Multimap löschen (da dort nur der Zeiger vorhanden ist), sondern auch noch das eigentliche Objekt deleten.
    Beispiel:

    Map::iterator it = ...; // Iterator auf das zu löschende Element
    delete *it;
    map.erase(it);
    

    Um an die Elemente einer Multimap zu kommen, schau dir mal die Methoden lower_range, upper_range und equal_range an.

    zu 4. (obwohl du ja eigentlich nur 3 Fragen hattest, aber erste hast du ja selbst beantwortet)
    Je nach STL-Implementierung gibt es auch Hash-(Multi)Maps, aber diese brauchst du erst zu verwenden, wenn die Performance der normalen Multimaps zu gering ist (d.h. wenn du einige Tausend oder mehr Objekte hast).
    Im kommenden C++ Standard soll es dann sogenannte "unordered maps" geben, welche den Hash-Maps entsprechen.

    Evtl. gibt es aber noch bessere Datentypen für dein Problem als die Multimap.
    Wenn das Spielbrett eine geringe Zahl an Positionen hat, würde ich eher ein Array (vector) verwenden und je Position eine Liste von Objekten:

    std::list<Base*> fields[MaxX][MaxY]; // alternativ auch std::vector
    

    So ich hoffe, das hilft dir weiter...
    Ich gehe jetzt ins Bett und evtl. schaue ich morgen noch mal hier rein -)



  • Wow, also ersteinmal abermals vielen Dank, ich werde mich direkt mal dran setzen, das zu implementieren.

    Deine Idee mit Array/Vector/Liste hatte ich auch schon, aber mein Spielfeld(ich mache ein Weltraum-Strategiespiel) hat definitiv viel zu viele Felder (ich mag es bei solchen Spielen, wenn der Maßstab riesig ist), als daß ich mir ein Array oder Vector leisten wollte. Ausserdem sind ja die meisten Felder eh leerer Weltraum, deswegen finde ich ne Multimap schon recht angemessen.

    Oder irre ich mich da? Wie gesagt, ich bin leider noch recht "wackelig" im Programmiergeschäft, und wenn du meinst, es gibt eine effizientere Methode, dann werd ich die sicher ausprobieren.



  • Wenn die Mehrheit deiner Felder leer ist, waere es wirklich nur Platzverschwendung, wenn du mit einem array/vector arbeitest, weil du dann ja fuer jedes einen leeren Eintrag (z.B null-pointer) anlegen muesstest.
    Alternativ zur Multimap kannst du auch eine normale Map von Listen verwenden, das waere dann der Mittelweg zwischen Multimap und dem vector<list<base*> >-Ansatz.
    Je nachdem bietet es sich evtl. auch an, die Objekte die sich an den entsprechenden Punkten befinden koennen als Komposita zu gestalten, so dass du mehrere davon zusammenfassen und das zusammengefasste Objekt an Stelle der list in die normale map packen kannst.



  • Also, ich habe jetzt mal versucht, den Vorschlag von Pumuckl zu implementieren, da ich *gehofft* habe, daß es mit den Lists in ner Map einfacher ist als mit ner Multimap.

    Hier der Code so far:

    #ifndef GALAXYMAP_HPP
    #define GALAXYMAP_HPP
    
    #include "Stellarobject.hpp"
    #include "Starsystem.hpp"
    #include "Coordinate.hpp"
    
    #include <map>
    #include <list>
    
    class Galaxymap
    {
    	public:
    		void add_item(Starsystem new_starsystem, int new_x_pos, int new_y_pos);
    		void remove_item(Stellarobject curr_object, int x_pos, int y_pos);
    
    	private:
    
    		int x_size;
    		int y_size;
    
    		typedef map<Coordinate, list<Stellarobject*> > Galaxy_map;
    		typedef Galaxy_map::value_type map_ype; 
    };
    #endif
    
    #include "Galaxymap.hpp"
    
    void Galaxymap::add_item(Starsystem cur_starsystem, int new_x_pos, int new_y_pos)
    {
    	//empty field in map?
    	if(Galaxy_map.find(Coordinate(new_x_pos,new_y_pos)) == Galaxy_map.end())
    	{
    		//yes, so a new list has to be created and the new item has to be put in it
    		list<Stellarobject*> new_list;
    		new_list.push_back(new Starsystem(cur_starsystem));
    		Galaxy_map.insert(map_type(Coordinate(new_x_pos,new_y_pos), new list<Stellarobject*>(new_list)));
    	}
    	else
    	{
    		//no, the item just has to be put into the existing list
    		list<Stellarobject*> existing_list = Galaxy_map.find(Coordinate(new_x_pos, new_y_pos));
    		existing_list.push_back(new Starsystem(cur_starsystem));
    		Galaxy_map.insert(map_type(Coordinate(new_x_pos,new_y_pos), new list<Stellarobject*>(existing_list)));
    	}
    }
    
    void Galaxymap::remove_item(Stellarobject curr_object, int x_pos, int y_pos)
    {
    	//Is there a list at the coordinates?
    	if(Galaxy_map.get(Coordinate(x_pos,y_pos)) == NULL)
    	{
    		//No, there is no list which means that there is nothing at all
    		cout << "Failed to find object at position" << x_pos << " " << y_pos << "(Map empty)" << endl;
    	}
    	else
    	{
    		//There is a list, so we have to search it for the object
    		list<Stellarobject*> temp_list = Galaxy_map.get(Coordinate(x_pos, y_pos));
    
    		list<Stellarobject*>:: iterator it;
    
    		bool delete_success = false;
    
    		for (it = temp_list.begin(); it != temp_list.end(); ++it)
    		{
    			if(it == curr_object)
    			{
    				delete *it;
    				temp_list.erase(it);
    				delete_success = true;
    			}
    		}
    		if(!delete_success)
    		{
    			cout << "Object wasn´t in the list" << endl;
    		}
    	}
    }
    

    Dazu habe ich jetzt wieder einige Fragen:

    1. Mein Compiler meint dazu:

    Galaxymap.cpp:6: error: expected primary-expression before '.' token

    und bezieht sich immerwieder damit auf Galaxy_map. Ist da was mit der typedef schiefgelaufen?
    Passend dazu kommt noch:

    Galaxymap.cpp:11: error: `map_type' undeclared (first use this function)

    Und dann würde ich gern wissen, wie ich das hier richtig schreiben muss, weil es nur so ne Art placeholder ist, damit ihr versteht, was das mal werden soll

    if(it == curr_object)
    

    Ich will damit erreichen, daß das if ausgeführt wird wenn der iterator auf dem objekt steht, das ich suche...



  • Ich werd mal deinen ganzen text quoten und Stueckweise meinen Senf dazu abgeben 😉

    Sceletor schrieb:

    [cpp]
    #ifndef GALAXYMAP_HPP
    #define GALAXYMAP_HPP

    #include "Stellarobject.hpp"
    #include "Starsystem.hpp"
    #include "Coordinate.hpp"

    #include <map>
    #include <list>

    class Galaxymap
    {
    public:
    void add_item(Starsystem new_starsystem, int new_x_pos, int new_y_pos);
    void remove_item(Stellarobject curr_object, int x_pos, int y_pos);

    Wenn du Zeiger auf Stellarobject in deinen Listen speicherst, sollte add_item evtl auch einen Zeiger oder eine Referenz auf ein solches uebernehmen, sonst musst du spaeter noch ein add_item(star_ship, int, int) und ein add_item(asteroid, int, int) usw. dazuschreiben. Ich wuerde einen Zeiger empfehlen, da sich der Aufrufer ja selbst um die Speicherverwaltung kuemmern muss und eine uebergabe per Referenz meist impliziert, dass das Objekt kopiert wird und der Container die Speicherverwaltung uebernimmt.
    Bei remove_item reciht auch eine Referenz oder ein Zeiger, es ist unnoetig da das Objekt zu kopieren (die Kopie querde in den meisten Faellen auch den Vergleihcsoperator fuer die Suche fehlschlagen lassen)

    private:

    int x_size;
    int y_size;

    typedef map<Coordinate, list<Stellarobject*> > Galaxy_map;
    typedef Galaxy_map::value_type map_ype; //TIPPFEHLER!!!
    };
    #endif
    [/cpp]

    #include "Galaxymap.hpp"
    
    void Galaxymap::add_item(Starsystem cur_starsystem, int new_x_pos, int new_y_pos)
    {
    	//empty field in map?
    	if(Galaxy_map.find(Coordinate(new_x_pos,new_y_pos)) == Galaxy_map.end())
    	{
    		//yes, so a new list has to be created and the new item has to be put in it
    		list<Stellarobject*> new_list;
    		new_list.push_back(new Starsystem(cur_starsystem));
    

    Wenn du hier tatsaechlich selbst die Speicherverwaltung in die Hand nehmen willst, ok... aber dann reicht immernoch eine Referenz als parameter, und du musst den ganzen Mist fuer andere stellarobjects nochmal schreiben (evtl. template)

    Galaxy_map.insert(map_type(Coordinate(new_x_pos,new_y_pos), new list<Stellarobject*>(new_list)));
    

    statt map_type geht auch atd::make_pair(), und da du eine map<Coordinate, list> hast, sollte da hinten kein new stehen.

    }
    	else
    	{
    		//no, the item just has to be put into the existing list
    		list<Stellarobject*> existing_list = Galaxy_map.find(Coordinate(new_x_pos, new_y_pos));
    		existing_list.push_back(new Starsystem(cur_starsystem));
    

    STOP! Du kopierst hier die Liste die du in deiner Map gefunden hast und packst das Neue Objekt in die Kopie, die nach der Funktion wieder geloescht wird. => Referenz auf die gefundene Liste benutzen.

    Galaxy_map.insert(map_type(Coordinate(new_x_pos,new_y_pos), new list<Stellarobject*>(existing_list)));
    

    letzteres kann dann wegfallen. Du versuchst hier naemlich eine Liste einzufuegen an eine Stelle, wo schon etwas war, das Einfuegen schlaegt daher fehl.

    }
    }
    
    void Galaxymap::remove_item(Stellarobject curr_object, int x_pos, int y_pos)
    {
    	//Is there a list at the coordinates?
    	if(Galaxy_map.get(Coordinate(x_pos,y_pos)) == NULL)
    

    Das funktioniert so nicht. Wenn an den Koordinaten nix ist, gibts auch keine Liste. Ausserdem handelst du mit einer map<Coordinate, List> und nicht mit einer map von pointern. Versuchs mit

    /*iterator*/ pos = Galaxy_map.find(Coordinate();
    if (pos == Galaxy_map.end()) //...
    

    im else-Zweig dann ueber den iterator weiterarbeiten

    {
    		//No, there is no list which means that there is nothing at all
    		cout << "Failed to find object at position" << x_pos << " " << y_pos << "(Map empty)" << endl;
    	}
    	else
    	{
    		//There is a list, so we have to search it for the object
    		list<Stellarobject*> temp_list = Galaxy_map.get(Coordinate(x_pos, y_pos));
    

    ueber den iterator von vorhin einfach auf die Liste zugreifen udn nicht unnoetig kopieren.

    list<Stellarobject*>:: iterator it;
    
    		bool delete_success = false;
    
    		for (it = temp_list.begin(); it != temp_list.end(); ++it)
    		{
    			if(it == curr_object)
    			{
    				delete *it;
    				temp_list.erase(it);
    				delete_success = true;
    			}
    		}
    

    versuchs mal mit std::find, das liefert dir auch gleich den list::iterator, fuer den du dann erase() aufrufen kannst

    if(!delete_success)
    		{
    			cout << "Object wasn´t in the list" << endl;
    		}
    	}
    }
    

    Dazu habe ich jetzt wieder einige Fragen:

    1. Mein Compiler meint dazu:

    Galaxymap.cpp:6: error: expected primary-expression before '.' token

    Welches ist Zeile 6 bei dir?

    und bezieht sich immerwieder damit auf Galaxy_map. Ist da was mit der typedef schiefgelaufen?
    Passend dazu kommt noch:

    Galaxymap.cpp:11: error: `map_type' undeclared (first use this function)

    Da hast du n tippfehler in der typedef

    Und dann würde ich gern wissen, wie ich das hier richtig schreiben muss, weil es nur so ne Art placeholder ist, damit ihr versteht, was das mal werden soll

    if(it == curr_object)
    

    siehe oben, std::find() nutzen. Das liefert dann list.end() zurueck, wenn das gesuchte objekt nicht in der liste ist

    Ich will damit erreichen, daß das if ausgeführt wird wenn der iterator auf dem objekt steht, das ich suche...



  • Sceletor, du hast mit der Zeile 'typedef ...' nur eine Typen-Definition erzeugst, aber noch keine Variable.

    typedef std::map<Coordinate, list<Stellarobject*> > Galaxy_map;
    typedef Galaxy_map::value_type map_type;
    
    Galaxy_map m_map; // <- Variable, d.h. diese mußt du in deinen Sourcen angeben!
    };
    

    Die typedefs benutzt man extra, damit man nicht immer explizit die komplette template-Definition hinschreiben muß (z.B. bei Iteratoren), d.h.

    Galaxy_map::iterator it = m_map.find(...);
    

    ist kürzer und eleganter als

    std::map<Coordinate, list<Stellarobject*> >::iterator = m_map.find(...);
    

    P.S. Ich habe in die Definition extra noch den Namensraum 'std' hinzugefügt, da alle C++ Standard Typen dort enthalten sind. (deswegen kommen evtl. deine Compiler-Fehler)



  • Ah, hatte mich gefragt, ob das typedef die deklaration übernimmt, aber mich dann zu blind auf deinen Code verlasse, Th..

    Ich habe jetzt mal versucht, die Änderungen von Pumuckel einzuarbeiten, aber ich hänge (den Compilermeldungen nach zu urteilen) immernoch bei den Deklarationen der Listen mit den Pointern.

    Da es wohl kaum Sinn mach, alles schnipselweise zu posten (am Ende lass ich was wichtiges aus) hier nochmal der gesamte Code.

    #ifndef GALAXYMAP_HPP
    #define GALAXYMAP_HPP
    
    #include "Stellarobject.hpp"
    #include "Starsystem.hpp"
    #include "Coordinate.hpp"
    
    #include <map>
    #include <list>
    
    class Galaxymap
    {
    	public:
    		void add_item(Stellarobject *new_stellar, int new_x_pos, int new_y_pos);
    		void remove_item(Stellarobject *curr_stellar, int x_pos, int y_pos);
    
    	private:
    
    		int x_size;
    		int y_size;
    
    		typedef std::map<Coordinate, list<Stellarobject*> > Galaxy_map;
    		typedef Galaxy_map::value_type map_type;
    		Galaxy_map Curr_map;
    };
    #endif
    
    #include "Galaxymap.hpp"
    
    void Galaxymap::add_item(Stellarobject *new_stellar, int new_x_pos, int new_y_pos)
    {
    	//empty field in map?
    	if(Curr_map.find(Coordinate(new_x_pos,new_y_pos)) == Curr_map.end())
    	{
    		//yes, so a new list has to be created and the new item has to be put in it
    		list<Stellarobject*> new_list;
    		new_list.push_back(new_stellar);
    		Curr_map.insert(map_type(Coordinate(new_x_pos,new_y_pos), list<Stellarobject*>(new_list)));
    	}
    	else
    	{
    		//no, the item just has to be put into the existing list
    		list<Stellarobject*> existing_list = Curr_map.find(Coordinate(new_x_pos, new_y_pos));
    		existing_list.push_back(new_stellar);
    	}
    }
    
    void Galaxymap::remove_item(Stellarobject *curr_stellar, int x_pos, int y_pos)
    {
    	//Is there a list at the coordinates?
    	list<Stellarobject*>::iterator it = Curr_map.find(Coordinate(x_pos,y_pos));
    
    	if(it = Curr_map.end())
    	{
    		//No, there is no list which means that there is nothing at all
    		cout << "Failed to find object at position" << x_pos << " " << y_pos << "(Map empty)" << endl;
    	}
    	else
    	{
    		//There is a list, so we have to search it for the object
    		list<Stellarobject*> temp_list = it;
    
    		list<Stellarobject*>::iterator new_it;
    
    		new_it = temp_list.find(curr_stellar);
    		if(new_it == temp_list.end())
    		{
    		cout << "Object wasn´t in the list" << endl;
    		}
    		else
    		{
    			delete *new_it;
    			temp_list.erase(new_it);
    		}
    	}
    }
    

    Und das sagt der Compiler dazu:

    Galaxymap.cpp: In member function void Galaxymap::add_item(Stellarobject*, int, int)': Galaxymap.cpp:16: error: conversion from \std::_Rb_tree_iterator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >' to non-scalar type `std::list<Stellarobject*, std::allocator<Stellarobject*> >' requested
    Galaxymap.cpp: In member function void Galaxymap::remove_item(Stellarobject*, int, int)': Galaxymap.cpp:26: error: could not convert((&it) <unknown operator> ((const std::_Rb_tree_iterator<std::pair<const Coordinate, std::list<Stellarobject, std::allocator<Stellarobject*> > > >&)((const std::_Rb_tree_iterator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >)(&(((std::map<Coordinate, std::list<Stellarobject, std::allocator<Stellarobject*> >, std::less<Coordinate>, std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > > >)((Galaxymap)this)) + 8u)->std::map<_Key, _Tp, _Compare, _Alloc>::end [with _Key = Coordinate, _Tp = std::list<Stellarobject*, std::allocator<Stellarobject*> >, _Compare = std::less<Coordinate>, _Alloc = std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >]()))))' to bool' Galaxymap.cpp:34: error: conversion from \std::_Rb_tree_iterator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >' to non-scalar type `std::list<Stellarobject*, std::allocator<Stellarobject*> >' requested
    Galaxymap.cpp:38: error: 'class std::list<Stellarobject*, std::allocator<Stellarobject*> >' has no member named 'find'
    Galaxymap.cpp:39: error: no match for 'operator==' in 'new_it == (&temp_list)->std::list<_Tp, _Alloc>::end [with _Tp = Stellarobject*, _Alloc = std::allocator<Stellarobject*>]()'
    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_tree.h:209: note: candidates are: bool std::_Rb_tree_iterator<_Tp>::operator==(const std::_Rb_tree_iterator<_Tp>&) const [with _Tp = std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > >]
    Galaxymap.cpp:45: error: type `struct std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > >' argument given to `delete', expected pointer
    Galaxymap.cpp:46: error: no matching function for call to std::list<Stellarobject*, std::allocator<Stellarobject*> >::erase(std::\_Rb\_tree_iterator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >&)' C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/list.tcc:96: note: candidates are: typename std::list<\_Tp, \_Alloc>::iterator std::list<\_Tp, \_Alloc>::erase(std::\_List\_iterator<\_Tp>) [with \_Tp = Stellarobject*, _Alloc = std::allocator<Stellarobject*>] C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl\_list.h:894: note: std::\_List\_iterator<\_Tp> std::list<\_Tp, \_Alloc>::erase(std::\_List\_iterator<\_Tp>, std::\_List\_iterator<\_Tp>) [with \_Tp = Stellarobject*, \_Alloc = std::allocator<Stellarobject*>] C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl\_function.h: In member functionbool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Coordinate]':
    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_tree.h:1102: instantiated from typename std::\_Rb\_tree<\_Key, \_Val, \_KeyOfValue, \_Compare, \_Alloc>::iterator std::\_Rb\_tree<\_Key, \_Val, \_KeyOfValue, \_Compare, \_Alloc>::find(const \_Key&) [with \_Key = Coordinate, \_Val = std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > >, \_KeyOfValue = std::\_Select1st<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >, \_Compare = std::less<Coordinate>, \_Alloc = std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >]' C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl\_map.h:498: instantiated fromtypename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = Coordinate, _Tp = std::list<Stellarobject*, std::allocator<Stellarobject*> >, _Compare = std::less<Coordinate>, _Alloc = std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >]'
    Galaxymap.cpp:6: instantiated from here
    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_function.h:227: error: no match for 'operator<' in '__x < __y'



  • Wie ich schon sagte, liegen alle C++ Standard Typen im Namensraum 'std'.

    Am besten du erzeugst dir für jede Typ-Definition ein eigenes typedef:

    typedef std::list<Stellarobject*> ObjectList; // sorry, hier hatte ich auch erst std:: vergessen (war natürlich nur ein Test, ob du es kapiert hast -)
    
    typedef std::map<Coordinate, ObjectList > Galaxy_map;
    

    Nun kannst du einfach

    ObjectList new_list;
    

    schreiben.

    Die restlichen Fehler muß du dann Zeile für Zeile durchgehen (oftmals werden dann Folgefehler angezeigt, die dann bei Eliminierung der ersten Fehler verschwinden).

    Aber nur durch die Fehler versteht man C++ richtig (auch wenn die Fehlerbeschreibungen nicht gerade einfach sind - insbesondere bei den komplexen template-Definitionen).

    P.S. sorry, hatte bei meinem ersten Posting die Variablen-Deklaration 'Map mmap' vergessen -)



  • Okay, ich bin meinen Code nochmal in Ruhe durchgegangen und habe ein paar Fehler gefunden.

    Was ich aber immernoch nicht verstehe, ist wie ich aus meiner Map nun die Liste bzw. aus der Liste das konkrete Objekt extrahiere.
    Mit find() erhalte ich ja nur einen Iterator, der auf den Key zeigt, an dem die Liste ist, oder einen Iterator, der auf das Ende der Map zeigt, wenn der Key nicht drin ist.

    Aber wie hole ich mit einem Iterator nun die Liste oder besser nen Zeiger auf die Liste?

    Das hier funktioniert einfach nicht, aber was ist daran falsch?

    Galaxy_map::iterator it = Curr_map.find(Coordinate(new_x_pos, new_y_pos));
    		Stellar_list existing_list = it;
    


  • Iteratoren kann man im Grunde wie Zeiger verwenden (* und -> Operator)



  • Könntest du das bitte etwas ausführen, d.h. mir sagen wie das in meinem konkreten Fall anzuwenden wäre?

    Folgendes

    Stellar_list existing_list = *it;
    

    habe ich schon vor einer Weile ausprobiert, und ich bekomme damit immernoch die Fehlermeldung

    Galaxymap.cpp:17: error: conversion from `std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > >' to 
    non-scalar type `std::list<Stellarobject*, std::allocator<Stellarobject*> >' requested
    


  • AAAARGL

    Ich habs....

    Stellar_list existing_list = it->second;
    

    Man muss natürlich aus dem Paar (Key,Value) den Value aussuchen...



  • Doch noch ne Frage:

    pumuckl schrieb:

    list<Stellarobject*>:: iterator it;
    
    		bool delete_success = false;
    
    		for (it = temp_list.begin(); it != temp_list.end(); ++it)
    		{
    			if(it == curr_object)
    			{
    				delete *it;
    				temp_list.erase(it);
    				delete_success = true;
    			}
    		}
    

    versuchs mal mit std::find, das liefert dir auch gleich den list::iterator, fuer den du dann erase() aufrufen kannst

    Ist das nicht falsch? Nach der Beschreibung der STL hat doch nur die map eine find() Funktion, nicht aber die List, oder?



  • Er redet wohl von std::find, einer Funktion aus dem Header <algorythm>.



  • Ah, okay, hab ich gerade auch eingebaut, und der Compiler ist zufrieden damit.

    Was mich aber immernoch verwundert, sind folgende Fehlermeldungen:

    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_function.h: In member function bool std::less<\_Tp>::operator()(const \_Tp&, const \_Tp&) const [with _Tp = Coordinate]': C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl\_tree.h:1102: instantiated fromtypename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::find(const _Key&) [with _Key = Coordinate, _Val = std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > >, _KeyOfValue = std::_Select1st<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >, _Compare = std::less<Coordinate>, _Alloc = std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >]'
    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_map.h:498: instantiated from `typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = Coordinate, _Tp = std::list<Stellarobject*, std::allocator<Stellarobject*> >, _Compare = std::less<Coordinate>, _Alloc = std::allocator<std::pair<const Coordinate, std::list<Stellarobject*, std::allocator<Stellarobject*> > > >]'
    Galaxymap.cpp:6: instantiated from here
    C:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_function.h:227: error: no match for 'operator<' in '__x < __y'

    Hier Zeile 6 der Galaxymap.cpp:

    if(Curr_map.find(Coordinate(new_x_pos,new_y_pos)) == Curr_map.end())
    

    Was bitte will mir der Compiler mit dem Salat sagen??

    EDIT:

    Habe hier im Forum was gefunden, bei nem Thread, wo jemand fast dasselbe vorhatte wie ich und auch denselben Fehler bekam. Als Lösung wurde folgendes gepostet:

    .filmor schrieb:

    Maps werden immer nach ihrem Schlüsseltyp sortiert. Damit das funktioniert, brauchst du ein Vergleichskriterium.
    Standardmäßig ist der Comparetyp std::less<KeyType> und std::less ist mit dem <-Operator definiert.
    Du hast also 3 Möglichkeiten, den Code zum Laufen zu bringen:
    1. Du schreibst einen <-Operator für MyClass (ist aber nur sinnvoll, wenn ein solcher Vergleich tatsächlich logisch möglich ist)
    2. Du spezialisierst das std::less-Template für deine Klasse
    3. Du schreibst eine eigene Vergleichsfunktion (das Sinnvollste, wenn du nur in einem Kontext eine solche Map anlegst.

    Mein Problem ist, daß ich leider keine der drei möglichen Lösungen nachvollziehen kann und auch nicht weiss, welche davon nun für mich die einfachste/effektivste wäre.

    Ich glaube zu verstehen, daß es darum geht, der STL beizubringen, wie sie meine Koordinaten nach Größe sortieren kann. Da wäre ja logisch einfach (entweder X oder Y Wert höher gewichten), aber wie und vor allem wo muss ich das implementieren.
    Wäre für Codebeispiele sehr dankbar! 🙄



  • Sceletor schrieb:

    .filmor schrieb:

    Maps werden immer nach ihrem Schlüsseltyp sortiert. Damit das funktioniert, brauchst du ein Vergleichskriterium.
    Standardmäßig ist der Comparetyp std::less<KeyType> und std::less ist mit dem <-Operator definiert.
    Du hast also 3 Möglichkeiten, den Code zum Laufen zu bringen:
    1. Du schreibst einen <-Operator für MyClass (ist aber nur sinnvoll, wenn ein solcher Vergleich tatsächlich logisch möglich ist)
    2. Du spezialisierst das std::less-Template für deine Klasse
    3. Du schreibst eine eigene Vergleichsfunktion (das Sinnvollste, wenn du nur in einem Kontext eine solche Map anlegst.

    Mein Problem ist, daß ich leider keine der drei möglichen Lösungen nachvollziehen kann und auch nicht weiss, welche davon nun für mich die einfachste/effektivste wäre.

    Ich glaube zu verstehen, daß es darum geht, der STL beizubringen, wie sie meine Koordinaten nach Größe sortieren kann. Da wäre ja logisch einfach (entweder X oder Y Wert höher gewichten), aber wie und vor allem wo muss ich das implementieren.
    Wäre für Codebeispiele sehr dankbar! 🙄

    Genau darum gehts, um ein Vergleichskriterium. Auf welcher Ebene du dieses Kriterium implementierst, ist die Sache die du entscheiden musst, und das sind die obigen drei Punkte. Wie du vielleicht weißt, hat die std::map nicht nur den key_type und den value_type als Templateparameter, sondern (neben dem Allocator, den alle container haben) auch einen sogenannten Comparator, das ist ein Functor, der zwei key_types miteinander vergleicht. Default für den Comparator ist std::less.

    Ich roll mal die Punkte von filmor von hinten auf: Punkt 3 bedeutet, einen eigenen Comparator zu schreiben. Das ist eine Klasse, die folgendes besitzen muss:
    - einen Standardkonstruktor (kannder implizit durch den COmpiler generierte sein)
    - einen bool operator()(const T&, const T&) , der die zu vergleichenden Objekte streng ordnet. Beispiel hierfuer wäre

    struct CoordCompare {
      bool operator() (const Coordinate& a, const Coordinate& b) {
        //Wenn x-Koordinaten gleich sind ordne nach y-Koordinaten, sonst nach x-Koordinaten
        return (a.x==b.x) ? (a.y<b.y) : (a.x<b.x);
      }
    };
    
    //benutzung dann wie folgt:
    std::map<Coordinate, Stellarobject*, CoordCompare>; //benutze CoordCompare fuer die Ordnung der Coordinates
    

    Zweite Möglichkeit ist, dem Compiler statt einem eigenen Comparator nur zu sagen, wie er std::less<> für Coordinaten zu verstehen hat, das waere dann eine Spezialisierung des templates:

    namespace std {
    template<>
    struct less<Coordinate> {
      bool operator() (const Coordinate& a, const Coordinate& b) {
        //Wenn x-Koordinaten gleich sind ordne nach y-Koordinaten, sonst nach x-Koordinaten
        return (a.x==b.x) ? (a.y<b.y) : (a.x<b.x);
      }
    };
    }
    
    //Nutzung:
    std::map<Coordinate, Stellarobject*>; //benutzt automatisch die Spezialisierung von std::less<Coordinate>
    

    Das sieht gleich aus, allerdings musst du jetzt bei Containern (und bei anderen Strukturen, die std::less<> nutzen) nicht mehr explizit den Comparator angeben sondern kannst dich darauf verlassen, dass der Compiler immer deine Spezialisierung findet.

    Das allgemeinste ist Punkt 1, nämlich die Angabe eines operator< für deine Coordinate Klasse. Damit kannst du nichtnur Abfragen wie "if (coordiA < coordiB)" realisieren. std::less benutzt nämlich defaultmäßig (das heißt, wenn man nicht wie in Punkt 2 ne Spezialisierung angibt) den operator< (Die Implementierung besteht meist aus der kleinen Zeile "return a<b;"). Deshalb hat dein Compiler auch gemeckert, weil std::less den op< für Coordinate aufgerufen hat, dens aber nicht gibt. Die Implementierung ist simpel:

    struct Coordinate {
    //...
    
    public:
      bool operator< (const Coordinate& other) const {
        //Wenn x-Koordinaten gleich sind ordne nach y-Koordinaten, sonst nach x-Koordinaten
        return (x==other.x) ? (y<other.y) : (x<other.x);
      }
    //...
    };
    

    (Bin mir grad nicht ganz sicher ob man op< auch außerhalb der Klasse mit zwei Argumenten implementieren kann und welche Version die Gurus da befürworten, aber so funktionierts auf jeden Fall)

    Alle drei Varianten funktionieren für deine map gleich gut.



  • Also, ich habe den Comparator nun implementiert und er läuft auch prima, aber wenn ich nun was (erstmal ein Starsystem) in die Map stecken will, kommt:

    matching function for call to `Galaxymap::add_item(Starsystem&, int&, int&)'
    Galaxymap.hpp:15: note: candidates are: void Galaxymap::add_item(Stellarobject*, int, int)

    und das, obwohl Starsystem (wie von Th erklärt) von der abstrakten Klasse Stellarobject erbt.

    Was muss ich an dem Code noch ändern?

    battlefield.add_item(new_System, x_pos, y_pos);
    
    class Starsystem : public Stellarobject
    
    #ifndef STELLAROBJECT_HPP
    #define STELLAROBJECT_HPP
    
    class Stellarobject
    {
    };
    
    #endif
    

    EDIT: Schon rausgefunden,

    battlefield.add_item(&new_System, x_pos, y_pos);
    

    war die Lösung.

    Wobei... Ist das richtig so? Und warum kann ich es nur als Referenz, nicht aber als Zeiger oder "ohne alles" (was wohl eine Kopie wäre dann) übergeben?



  • Was ist 'new_system' denn?
    Anscheinend eine lokale Variable vom Type 'Starsystem'.
    Da du aber Zeiger auf Stellarobjekte in deiner Liste verwaltest, mußt du ein mit 'new' allokiertes Starsystem übergeben.
    Andernfalls wäre das Objekt ja nur temporär vorhanden (ab Blockende wäre der Speicher wieder überschrieben).
    Hier nun die korrekte Version:

    Starsystem *new_system = new Starsystem(...); // Parameter?
    battlefield.add_item(new_System, x_pos, y_pos);
    


  • Wie schon oben angedeutet musst du dich fuer dein Design fuer eine von zwei Varianten entscheiden, was die Speicherung angeht:
    entweder, du uebergibst deinem Container einen Zeiger auf ein bestehendes Objekt, oder das Objekt selbst. Wenn du ihm einen Zeiger uebergibst, sollte das kein Zeiger auf ein lokales Objekt sein, da sonst irgendwann ein Zeiger auf ein nichtmehr existierendes Objekt in deinem Container rumliegt. Daher musst du wie th schon sagt die Objekte mit new erzeugen (delete spaeter nicht vergessen!).
    Wenn du ihm das Objekt selbst zum Kopieren und Einfuegen uebergeben willst, gibt es zwei Probleme:
    erstens muesstest du fuer jede Klasse, von denen du Objekte in den Container legen willst, eine eigene Einfuegemethode schreiben. Denn sowas wie add_item(Stellarobject& newobject) funktioniert nicht, da dann nur der Basisklassenteil des Objekts kopiert wuerde und alle zusaetzlichen informationen verloren gehen (splicing). Zweitens kannst du in einen Container nicht Objekte verschiedenen Typs packen, also musst du trotzdem mit Pointern auf stellarobject arbeiten.
    Falls du (berechtigterweise) davor zurueckschreckst, die Objekte alle mit new und delete selbst verwalten zu muessen, gibts einen oft genutzten Ansatz: Du gibst der Klasse stellarobject die vieruelle methode Clone() mit, die das jeweilige Objekt dupliziert und einen Zeiger darauf zurueckgibt. Der add_item methode gibst du dann einen Zeiger auf das (moeglicherweise lokale) Objekt, die methode ruft davon Clone() auf, um einen Klon zu erstellen, der laenger als das lokale Objekt lebt. Das deleten dieses KLons muss dann der verwaltende Container uebernehmen.


Anmelden zum Antworten