Problem mit List, Iterator und Pointern auf Objekte



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    Ich glaube ich habe Schwierigkeiten zu verstehen ob der Iterator eine Position oder ein Pointer auf ein Objekt ist.

    Man kann einen Iterator als Verallgemeinerung solcher Konzepte wie "Pointer" oder "Position" betrachten. "Position" passt eigentlich ganz gut, allerdings nicht im konkreten Sinne z.B. eines Array-Index, sondern eher als abstrakte Position.

    Bei meinem Beispiel hier wird dann also der Iterator im Löschfall auf das Ende gesetzt ? Also wäre m_ItShot = m_ShotList.erase(m_ItShot); genau so wie m_ItShot = m_ShotList.end(); ?

    Nein. erase() gibt einen Iterator auf das nachfolgende Element zurück. Nur wenn es kein solches gibt, weil du das letzte Element gelöscht hast, wird der end()-Iterator zurückgegeben.

    Ansonsten: Ausschlafen und nochmal in Ruhe ansehen. Vor allem die Anmerkungen zu dem unnötigen und fehlerhaft verwendeten new.



  • 
    // Dieses habe ich als Membervariablen der Klasse CGame festgelegt:
    	list<CSprite> m_ShotList;
    	list<CSprite>::iterator m_ItShot;
    
    // Dies rufe ich in der Spawn Funktion auf:
    	if(m_bFire && m_pShotTimer->Timeout()) {
    	CSprite pShot(180,8.0f);
    	pShot.Load("img/schiff/shotimage2.png");
    	pShot.SetPos(m_pRaumschiffLinksRechts->GetPos().x + 160,m_pRaumschiffLinksRechts->GetPos().y + 120);
     	m_ShotList.push_back(pShot);		
    	}
    

    So sieht das jetzt mit dem erase aus:

    while(m_ItShot != m_ShotList.end()) {
    
    for(m_ItBallon = m_BallonList.begin(); m_ItBallon != m_BallonList.end(); ++m_ItBallon) {
    						
    	if(Entfernung(m_ItShot->GetPos().x,m_ItShot->GetPos().y,m_ItBallon->GetPos().x,m_ItBallon->GetPos().y) <= 30.0f) {
    		m_ItShot = m_ShotList.erase(m_ItShot);
    		m_nPunktestand += 30;
    	} else {
    		++m_ItShot;
    	}
    }}
    

    Leider wird nichts mehr gerendert.

    // So iteriere ich durch die Liste:
    	m_ItShot = m_ShotList.begin();
    	while( m_ItShot != m_ShotList.end()) {
    		m_ItShot->Render(0);
    		++m_ItShot;
    	}
    

    Ich weiß nicht mehr weiter 😭



  • Was soll denn jetzt die for Schleife in der while Schleife?



  • Also ich hätte noch einen weiteren allgemeinen Tipp zu deinem ersten Beispiel (ist das jetzt gelöst? Der neue Code sieht ja doch ganz anders aus!). Und zwar würde ich da noch empfehlen, dieses Riesen-if hier (if( m_ItShot->OL().x <= 0 || m_ItShot->OL().x >= SCREEN_WIDTH || m_ItShot->OL().y <= -100 || m_ItShot->OL().y >= SCREEN_HEIGHT ) {) in eine Funktion zu schreiben. Zum Beispiel bool hasLeftScreen(const Sprite &) (oder auch andersrum: isOnScreen, je dachdem, was besser passt). Dann kann man sowas wie myList.erase(remove_if(begin(myList), end(myList), hasLeftScreen), end(myList)) schreiben und sofort ist der Befehl klar (und vor allem richtig, ohne dass du über einen Index oder sowas nachdenken musst).

    Was willst du jetzt in deinem neuen Quelltext tun - jeden Shot mit jedem Ballon vergleichen? Jedes mit jedem klingt immer irgendwie gefährlich langsam, vor allem wenn du dann noch Listen hast (hast du eigentlich einen guten Grund für std::list? Aber andere Baustelle...). Wenn beides wenige Elemente enthält, dann vergleiche meietwegen auch alles mit allem. Aber dann wäre es vielleicht hilfreich, die innere Schleife in eine Funktion zu tun und zu benennen.



  • Was willst du jetzt in deinem neuen Quelltext tun - jeden Shot mit jedem Ballon vergleichen? Jedes mit jedem klingt immer irgendwie gefährlich langsam

    Ja das stimmt. Ich muss alles nochmal neu überdenken. Danke für die Hilfe. Ich melde mich mal falls ich weiter gekommen bin.



  • Ich habe jetzt wirklich viel herumprobiert und komme zu dem Ergebnis das die folgende Methode
    Pointer von Objekten in einer Liste zu hinterlegen als einzige in meinem Projekt zu gerenderten Ergebnissen
    führt. Ich muss wohl eher einen Fehler gemacht haben als ich die vielen verschachtelten Schleifen erstellt habe.
    Wenn ich die Kollisionsschleifen in meiner Gameschleife auskommentiere bekomme ich auch keine Fehlermeldung.

    list<CObjekt> ObjektListe;
    list<CObjekt>::iterator ObjektIterator;
    
    while( TimerA >= TimerB) {
       CObjekt *pPointerX = new CObjekt( BspWertA, BspWertB );
       pPointerX->Memberfunktion("BspDateiname.png");
       ObjektListe.push_back(*pPointerX);
       TimerA = 0;
    }
    
    for(ObjektIterator = ObjektListe.begin(); ObjektIterator != ObjektListe.end(); ++ObjektIterator) {
       ObjektIterator->Update();
       ObjektIterator->Render();
    }
    
    ObjektListe.clear();
    


  • Warum "new"? Du hast da einen schönen Memory Leak produziert.



  • Wie teste ich mein Programm effektiv auf Memory Leaks ? Etwa mit VS 2019 Community ?

    Link Text



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    Pointer von Objekten in einer Liste zu hinterlegen

    Du hinterlegst keine Pointer!



  • Habe folgende Anleitung gefunden:
    Memory Leaks finden

    Und Ja es gibt welche:
    Screenshot



  • @_andi84 Für Linux gibt es Valgrind, was da recht mächtig ist. Für Windows gibt es auch tools, die nach Speicher Problemen suchen können, siehe vlt: https://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows

    Regel Nummer 1 um Memory Leaks zu verhindern: Keine manuelle Speicherverwaltung (kein new)!



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    Wie teste ich mein Programm effektiv auf Memory Leaks ?

    Das Memory Leak ist in der while Schleife. Da braucht man nichts zu suchen!



  • Wie kann ich denn auf die Objekte zugreifen wenn ich die ohne new direkt als ganzes Objekt in der Liste hinterlege ?
    Weil Iterator->Methode() funktioniert irgendwie nicht.



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    funktioniert irgendwie nicht

    ist keine Fehlerbeschreibung



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    while( TimerA >= TimerB) {
    CObjekt *pPointerX = new CObjekt( BspWertA, BspWertB );
    pPointerX->Memberfunktion("BspDateiname.png");
    ObjektListe.push_back(*pPointerX);
    TimerA = 0;
    }

    Was spricht denn gegen:

    while( TimerA >= TimerB) {
       auto object = CObjekt( BspWertA, BspWertB );
       object.Memberfunktion("BspDateiname.png");
       ObjektListe.emplace_back(object);
       TimerA = 0;
    }
    

    Das hat nichts mit Iteratoren zu tun. Iteratoren sind, wie der Name sagt, zum iterieren durch Container und sind irgendwie eine Referenz auf die entsprechende Position im Container, d.h. du kannst über den Iterator auf deine Objekte zugreifen.



  • ...und wenn dein CObjekt Probleme beim Kopieren macht, dann machst du statt deiner std::list<CObject> eben eine std::list<std::unique_ptr<CObject>>. Dann kannst du sowas machen und brauchst kein delete:

    auto pPointerX = std::make_unique<CObjekt>( BspWertA, BspWertB );
    pPointerX->Memberfunktion("BspDateiname.png");
    ObjektListe.push_back(std::move(pPointerX));
    

    (und warum eigentlich std::list statt std::vector - sicher dass du eine Liste brauchst?)



  • @wob
    wenn ich mir die Kopie sparen wollen würde, würde ich wohl sowas machen:

    while( TimerA >= TimerB) {
       ObjektListe.emplace_back(BspWertA, BspWertB );
       ObjektListe.back().Memberfunktion("BspDateiname.png");
       TimerA = 0;
    }
    

    Aber ich glaube, dass, wenn CObjekt nicht Kopierbar ist bzw da Probleme macht, _andi84 noch ganz andere Probleme bekommt.

    Und, weil ich es auch nicht verstehe: Warum list und nicht vector?



  • Ich habe es jetzt hinbekommen:

    So erzeuge ich die Objekte (heißen inzwischen CParticle 😉 ):

                           if (Zeitgeber.GetTimerTicks() >= SpawnTime) {
    
    				SDL_Point StartPos = { 400,300 };
    
    				srand(SDL_GetTicks());
    
    				float fAngle = (float)(rand() % 360);
    
    				SDL_Log("%f", fAngle);
    
    				CParticle* pParticle = new CParticle(StartPos, 0.5, fAngle);
    
    				ParticlesList.push_back(pParticle);
    
    				SpawnTime = Zeitgeber.GetTimerTicks() + 1;
    			}
    

    So werden diese aufgerufen:

                           for (myParticle = ParticlesList.begin(); myParticle != ParticlesList.end(); ++myParticle) {
    				(*myParticle)->Update();
    				(*myParticle)->Render();
    			}
    

    Und so lösche ich diese wieder:

            for (myParticle = ParticlesList.begin(); myParticle != ParticlesList.end();) {
    		delete(*myParticle);
    		myParticle = ParticlesList.erase(myParticle);
    	}
    

    Hoffe es hilft vielleicht irgendwem weiter. Gruß _andi84



  • Warum new und delete? Das ist nicht nötig und sollte vermieden werden.



  • @_andi84 sagte in Problem mit List, Iterator und Pointern auf Objekte:

    srand(SDL_GetTicks());

    Nein! Einmal!


Log in to reply