Objekt anhand von Membervariable in vector finden



  • Moin Moin alle zusammen.

    ich bin gerade im Begriff mir eine minimale 2D-Engine zu schreiben, an sich funktioniert sie auch schon ganz gut, aber um jetzt nicht alles Hardcore zu programmieren, wollte ich das laden von Sprites automatisieren. Ich erstelle ein Sprite, verpasse ihm einen Namen, und bringe es in den vector, so weit so gut. Nun möchte ich eine Funktion schreiben die mir das Objekt im vector anhand des Namens findet und dieses return damit ich auf die Memberfunktionen zugreifen kann um z.B. die Position ändern zu können.

    Meine Frage ist nun: Wie mach ich das am geschicktesten?

    Schon mal danke im voraus.



  • Indem du eine geeignete Datenstruktur verwendest: std::map.



  • Okay, in wie fern ist map besser?
    Ich habe bisher leider nur die list verwendet und selbst da habe ich nur durch iteriert. Auf vector bin ich nur gekommen weil das mir ein Kollege empfohlen hat.



  • Map ist ein assoziativer Container. Das heisst, du speicherst einen Key ab, welcher ein Value identifiziert. Über den Key bekommst du Zugriff auf den Value.
    Anders ausgedrückt ist der Key der Index des Values.

    using namespace std;
    
    map<string, Bild> map;
    map["schuss.jpg"] = LoadBild("Schuss.jpg");
    // ...
    
    map["Schuss.jpg"].wichzeichner(1.5f);
    

    so in der Richtung sieht das dann aus.
    Guck einfach mal in einer beliebigen C++ Referenz.



  • Ich erstelle ein Sprite, verpasse ihm einen Namen, und bringe es in den vector, so weit so gut.

    Anderer Vorschlag: Du erstellst die Sprite. Du erstellst einen Namen, und dann fügst du die Sprite in die Map ein.

    Natürlich entfernst du dann das String-Attribut aus der Sprite -Klasse. Das wäre redundant. Stattdessen hält eine Art von Manager das Objekt, e.g. SpriteManager . Der hat die Map. Und jede Sprite hält eine Referenz, bzw. Zeiger auf ihren Erzeuger und einen Iterator auf sich selbst. So kann jede Instanz einfach auf ihren Namen zugreifen, auf ihren Erzeuger und auf ihren Iterator...



  • Also ich würde aber aufpassen. map ist gar nicht Mal so schnell und schafft Overhead. Du sprichst von Sprites. Dann willst Du bestimmt in jedem Frame eine ganze Menge von denen rendern. Wenn Du da jedes Mal über [] das Element suchen musst, verlierst Du gut Performance. Daher würde ich zumindest noch je Szene alle benötigten Sprites cachen. Vielleicht macht man das auch anders und geschickter, keine Ahnung, aber die Suchoperation würde ich wegoptimieren.



  • Sone schrieb:

    Anderer Vorschlag: Du erstellst die Sprite. Du erstellst einen Namen, und dann fügst du die Sprite in die Map ein.

    Natürlich entfernst du dann das String-Attribut aus der Sprite -Klasse. Das wäre redundant. Stattdessen hält eine Art von Manager das Objekt, e.g. SpriteManager . Der hat die Map. Und jede Sprite hält eine Referenz, bzw. Zeiger auf ihren Erzeuger und einen Iterator auf sich selbst. So kann jede Instanz einfach auf ihren Namen zugreifen, auf ihren Erzeuger und auf ihren Iterator...

    Der SpriteManager existiert und funktioniert bereits, er lädt die Informationen aus einer XML und erstellt anhand diesen das Objekt, das Problem war halt bisher das ich nicht explizit ein Spritobjekt manipulieren konnte weil ich es nicht auf die Reihe bekommen habe es aus dem vector zu bekommen. Ich lese mich mal in map ein und bastle mal ein bisschen herum, wenn ich nicht weiterkomme weiß ich ja wo ich fragen muss 🙂

    Eisflamme schrieb:

    Also ich würde aber aufpassen. map ist gar nicht Mal so schnell und schafft Overhead. Du sprichst von Sprites. Dann willst Du bestimmt in jedem Frame eine ganze Menge von denen rendern. Wenn Du da jedes Mal über [] das Element suchen musst, verlierst Du gut Performance. Daher würde ich zumindest noch je Szene alle benötigten Sprites cachen. Vielleicht macht man das auch anders und geschickter, keine Ahnung, aber die Suchoperation würde ich wegoptimieren.

    Deshalb hat mir mein Kollege vector vorgeschlagen weil er auf den zufälligen Zugriff ja optimiert ist. Für einfaches iterieren würde sich ja list anbieten aber man muss sich halt mal für was entscheiden.



  • Vielleicht schaffst du es die Spritenamen durch fortlaufende Nummern zu ersetzen, dann kannst du per vector[nummer] auf die Sprites zugreifen. Ansonsten würde ich mich nicht von Quatsch alla "map ist inperformant" verwirren lassen, zumal du bisher durch eine list iteriert hast. map ist schon gut schnell und wenn das Projekt fertig ist und ein kleines bisschen schneller laufen soll kannst du immernoch was eigenes bauen.



  • Ich glaube nicht, dass man so extrem oft über die ganze Spriteslist durchsucht, sondern wohl nur beim Laden und danach behält man die pointer.



  • Ich kann Sachen hinzufügen aber ich kann leider keine Sachen per .find aus der map holen, ich bekomme da immer dann diese Meldung:

    Debug Assertion Failed!

    Expression: map/set iterator not dereferencable

    Ich kann mir auch schon denken was das Problem ist und zwar das der key ein string ist, aber ich habe im Moment keine Ahnung wie ich das am besten lösen soll.



  • Ein String als Key ist kein Problem.
    Ich würde raten, dass der Key nicht existierte und dir deshalb die map map::end als Iterator gegeben hat, den man nicht dereferenzieren kann.
    Wenn du weißt, dass das Objekt existiert oder einen automatischen Default-Konstruktor hat, dann benutzte [] für den Zugriff. Zeig mal ein Beispiel wo der Compiler meckert.



  • std::map<STRING, CSprite*> mSprites;
    
    int CSpriteManager::AddSprite( STRING _filename, STRING _spriteName )
    {
    	mSprites.insert( std::pair<STRING, CSprite*>( _spriteName , new CSprite( _filename ) ) );
    
    	return 0;
    }
    
    CSprite CSpriteManager::GetSpriteByName( STRING _spriteName )
    {
    	return mSprites.find( _spriteName )->first;
    }
    

    Diese Zeilen bereiten mir gerade Kopfzerbrechen, wie gesagt, das hinzufügen funktioniert, das GetSpriteByName nicht.



  • std::pair<STRING, CSprite*>(_filename , new CSprite( _filename ) )
    

    Und was, wenn im Konstruktor von pair irgendwas eine Exception wirft (copy-ctor von STRING, bspw.)? Hier natürlich kein elementares Problem, aber du solltest auf jeden Fall mal über das Grundproblem nachdenken.

    mSprites.find( _spriteName )->first
    

    Du möchtest wohl eher den Member second .



  • Sone schrieb:

    std::pair<STRING, CSprite*>(_filename , new CSprite( _filename ) )
    

    Und was, wenn im Konstruktor von pair irgendwas eine Exception wirft (copy-ctor von STRING, bspw.)? Hier natürlich kein elementares Problem, aber du solltest auf jeden Fall mal darüber nachdenken.

    mSprites.find( _spriteName )->first
    

    Du möchtest wohl eher den Member second .

    pair funktioniert, da gibt es keine Probleme, beim zweiten hast recht, ich möchte das second 🙂 . Inzwischen konnte ich das Problem lösen, bitte fragt nicht wie denn ich weiß es selber nicht ganz so genau, ich habe einfach alles gelöscht und frisch gecoded, dann lief es.

    Auf jedenfalls einmal ein großes Danke!
    Vielleicht brauche ich nochmals eure Hilfe...



  • nwp3:
    Ich bin nicht sicher, wie hoch die Performance von map ist, ich halte nur nichts davon in jedem Frame über [] eine Suche anzustoßen, das sollte Direktzugriff über Random-Access oder Cache-Zugriff sein. Für Suchen hat man i.d.R. keine Zeit. Geschieht dies nicht in jedem Frame, habe ich ja nichts gesagt.


Log in to reply