Vektoren und mehrere Objekte führen zum Crash
-
Hallo,
ich habe einen Programmierfehler, den ich jedoch auch nach gründlicher Suche bisher nicht ausfindig machen konnte. Ich hoffe ich finde hier mit euch die Lösung des Problems.
Zunächst habe ich eine abstrakte Klasse CObject, von der CParticle abgeleitet ist.
class CObject { public: CObject( eIdentifier id); virtual void Init(); virtual void Uninit(); virtual void Draw( SCamera *camera) = 0; virtual void Idle( DWORD dwDeltaTime ) = 0; /* ... */ }; typedef std::vector< CObject* > vecObjectPtr;Während Idle die Position des Objects aktualisiert, bringt Draw das Object auf den Bildschirm.
Nun gibt es noch einen ObjectManager, der alle Objekte organisiert:class CObjectManager { public: static CObjectManager* GetInstance(); static void DestroyInstance(); CObject* Add( CObject* object); void CleanUp(); void Idle( DWORD dwDeltaTime ); void Draw( SCamera *camera); private: CObjectManager(); ~CObjectManager(); static CObjectManager* m_pInstance; vecObjectPtr m_vecObjects; }; #define OBJECTMGR CObjectManager::GetInstance()Drei der Funktionen sind wie folgt definiert:
CObject* CObjectManager::Add( CObject* object ) { m_vecObjects.push_back( object ); return object; } // --------------------------------------------------------------------------- void CObjectManager::Idle( DWORD dwDeltaTime ) { vecObjectPtr::iterator it1, it2; vecObjectPtr::iterator itEnd; CObject *object, *compObject; for( it1 = m_vecObjects.begin(); it1 != m_vecObjects.end(); ) { object = *it1; if( object->GetDestroy() ) // wenn zerstört werden soll -> löschen { delete object; it1 = m_vecObjects.erase( it1 ); } else { ++it1; } } itEnd = m_vecObjects.end(); for( it1 = m_vecObjects.begin(); it1 != itEnd; ++it1 ) { object = *it1; // Kollisionsabfrage for( it2 = m_vecObjects.begin(); it2 != itEnd; ++it2 ) { if( it1 != it2 ) { compObject = *it2; if( object->CollidesWith( compObject )) object->HandleCollision( compObject); } } } itEnd = m_vecObjects.end(); for( it1 = m_vecObjects.begin(); it1 != itEnd; ++it1 ) { object = *it1; object->Idle( dwDeltaTime ); } } // --------------------------------------------------------------------------- void CObjectManager::Draw( SCamera *camera) { CObject* object; for( vecObjectPtr::iterator it = m_vecObjects.begin(); it != m_vecObjects.end(); ++it ) { object = *it; object->Draw( camera); } }Nun wird in einer Schleife immer nacheinander Idle und Draw ausgeführt, zwischendurch kann der Benutzer (mit der Funktion Add) neue Objecte hinzufügen.
Wenn man nun einzelne neue Objecte hinzufügt, funktioniert alles. Sobald aber mehrere Partikel hintereinander eingefügt werden sollen, bricht das Programm ab.
Der Grund: Der Iterator (im Manager) erreicht ungültige Werte.

Wie kann das sein, ich bleibe doch immer im Bereich zwischen begin und end?Anm: Das itEnd in Idle hatte ich vorerst direkt als m_vecObjects.end(), dann konnte ich gar keine neuen Objekte hinzufügen!
Vielen Dank für eure Mühe!
-
Gast5555 schrieb:
Der Grund: Der Iterator (im Manager) erreicht ungültige Werte.
Ist dir bewusst, das Änderungen am Vektor seine Iteratoren ungültig macht (z.B. erase)?
std::remove_if und anschließendes erase könnten dir helfen.
Am Beispiel:
std::vector<int> values; values.push_back(1); values.push_back(2); values.push_back(3); values.push_back(4); // Alle Elemente nach hinten sortieren, die kleiner 3 sind std::vector<int>::itNewEnd = std::remove_if(values.begin(), values.end(), std::bind2nd(std::less<int>(), 3)); // Elemente anschließend löschen values.erase(itNewEnd , values.end());
-
Ist dir bewusst, das Änderungen am Vektor seine Iteratoren ungültig macht (z.B. erase)?
Ja, aber die einzigen Änderungen am Vektor sind die in den Funktionen dargestellten (zusätzlich zu CleanUp, mit der zum Aufräumen alle Objekte einfach gelöscht werden).
Wie gesagt, das was den Fehler auslöst ist eine Schleife wie diese:
for( int i = 0; i < Max; i++) { /* ... */ part = new CParticle( ...); OBJECTMGR->Add( part); }
-
Der Destruktor von CObject sollte virtuell sein!
-
Am Destruktor liegt es wohl nicht, ich habe mal einen virtuellen zu CObject hinzugefügt (hatte bisher keinen selbstdefinierten), jedoch ohne Erfolg.
Das Problem muss an anderer Stelle entstehen.
-
Sind mehrere Threads im Spiel?
-
Gast5555 schrieb:
Am Destruktor liegt es wohl nicht, ich habe mal einen virtuellen zu CObject hinzugefügt (hatte bisher keinen selbstdefinierten), jedoch ohne Erfolg.
Das Problem muss an anderer Stelle entstehen.
Dann bitte einmal das übliche: Den Code aufs Allerwesentlichste reduzieren, d.h. schrittweise Klassen entfernen, die nichts mit der Fehlermeldung zu tun haben, die anderen Klassen so weit wie möglich aushöhlen, bis nurnoch relevante Funktionalität übrig ist. Zwischendurch immer testen, ob der Fehler noch besteht, und am Ende den vollständigen compilierfähigen Code posten.
-
theta schrieb:
Sind mehrere Threads im Spiel?
Nein, das Hauptprogramm ruft einfach immer wieder Idle auf, bis die Zeit für Draw überschritten ist (um eine bestimmte Frequenz der Bilder zu erreichen).
pumuckl schrieb:
Dann bitte einmal das übliche: Den Code aufs Allerwesentlichste reduzieren, d.h. schrittweise Klassen entfernen, die nichts mit der Fehlermeldung zu tun haben, die anderen Klassen so weit wie möglich aushöhlen, bis nurnoch relevante Funktionalität übrig ist. Zwischendurch immer testen, ob der Fehler noch besteht, und am Ende den vollständigen compilierfähigen Code posten.
Scheinbar liegt das Problem tiefer als ich angenommen habe?!
Ich bin weiterhin auf Fehlersuche, bisher konnte ich die Ursache noch nicht eingrenzen, da das Projekt schon recht umfangreich geworden ist.
-
Gast5555 schrieb:
theta schrieb:
Sind mehrere Threads im Spiel?
Nein, das Hauptprogramm ruft einfach immer wieder Idle auf, bis die Zeit für Draw überschritten ist (um eine bestimmte Frequenz der Bilder zu erreichen).
Das heißt es ist sicher, daß das Programm nicht irgendwie mitten in der Idle()-Methode unterwegs ist, wenn jemand Add() aufruft?
vector<>::push_back() gehört zu den Funktionen, die unter gegebenen Umständen alle Iteratoren ungültig machen können. Das heißt, wenn du beim Aufruf noch Iteratoren hast, zeigen diese anschließend ins Nirvana.
-
vector<>::push_back() gehört zu den Funktionen, die unter gegebenen Umständen alle Iteratoren ungültig machen können. Das heißt, wenn du beim Aufruf noch Iteratoren hast, zeigen diese anschließend ins Nirvana.
Das bedeutet, wenn ich von einem Object innerhalb der Idle-Routine das Add aufrufe, wird der Iterator ungültig?! - Dann haben wir wohl das Problem geortet.
Aber wie kann ich das denn sonst machen, es muss doch eine Möglichkeit geben?
Vielleicht die Idle-Funktionen der Objekte mit einem Rückgabewert versehen!?
-
Gast5555 schrieb:
vector<>::push_back() gehört zu den Funktionen, die unter gegebenen Umständen alle Iteratoren ungültig machen können. Das heißt, wenn du beim Aufruf noch Iteratoren hast, zeigen diese anschließend ins Nirvana.
Das bedeutet, wenn ich von einem Object innerhalb der Idle-Routine das Add aufrufe, wird der Iterator ungültig?! - Dann haben wir wohl das Problem geortet.
Nicht bei jedem Aufruf, aber auf lange Sicht passiert es auf jeden Fall einmal. vector<> speichert seine Daten in einem zusammenhängenden Speicherbereich und wenn der voll ist, wird alles in einen größeren Bereich umkopiert (und die Iteratoren bekommen dieses Umkopieren nicht unbedingt mit).
Aber wie kann ich das denn sonst machen, es muss doch eine Möglichkeit geben?
Vielleicht die Idle-Funktionen der Objekte mit einem Rückgabewert versehen!?Eine Möglichkeit wäre es, auf einen anderen Container auszuweichen - std::list<> macht nur Iteratoren ungültig, wenn das dahinterliegende Element gelöscht wird.
-
Okay, schonmal vielen Dank!
Ich habe jetzt den vector zu einer list gemacht - und es scheint besser zu funktionieren. Allerdings ist das Programm trotzdem wieder abgestürtzt, ob es allerdings noch an dem Container bzw. den Iteratoren lag, kann ich nicht sagen.Ich werde also weiter testen. Nur weiss ich noch nich, ob ich auf fehlerfreien Betrieb hoffen soll, oder mir gerade Abstürze wünschen sollte ...

Wieso kann ich nicht einfach nur programmieren, anstatt ständig auf der Suche nach Bugs zu sein?!
-
Ich verstehe Dich nicht. Debugge doch einfach - fixe den Fehler und gut ist!
-
Wenn es so einfach wäre den Fehler zu reproduzieren, gäbe es ja auch kein Problem!
Als ich getestet habe und das Programm abgestürtzt ist, habe ich nicht debugged.Ich habe also noch nicht herausgefunden, in welchem Fall dieser Fehler auftritt, es scheint keine feste Regel zu haben.

-
vll hilft dir ja ein Logging in den Funktionen, dann weißte genaueres wo er ausgestiegen ist.
greetz KN4CK3R
-
Ich habe das Programm jetzt ausführlich mit meinem Bruder getestet. Es scheint nun keinen Fehler mehr mit den Iteratoren zu geben, somit betrachte ich das Problem als gelöst.
KN4CK3R schrieb:
vll hilft dir ja ein Logging in den Funktionen, dann weißte genaueres wo er ausgestiegen ist.
Ja, ein Logging hab ich in den meisten Funktionen mit drin, das hat sich auch schon als sinnvoll herausgestellt. Aber die Sachen, die ständig wiederholt aufgerufen werden (wie Draw oder Idle in meinem Code) lasse ich dann doch lieber frei davon - sonst würde die Performance völlig zusammenbrechen.
-
sowas macht man ja auch dynamisch abschaltbar

greetz KN4CK3R