Segmentation fault beim Deleten eines Pointers
-
@daniel Zeig' bitte mal ein minimales kompilierbares Beispiel mit dem sich das Verhalten reproduzieren lässt.
@wob sagte in Segmentation fault beim Deleten eines Pointers:
Schon. Aber es ist extrem schwierig, das 100% korrekt hinzubekommen (auch unter dem Stichwort "Exception safety").
Das. Weil wenn Du das dann wirklich exception-safe hast, hast Du einen Smartpointer nachprogrammiert.
-
@manni66 Ich glaube der Fehler liegt darin, dass im Mainloop, welcher über alle State Pointer iteriert, nachdem der Pointer deleted und im Vector entfernt wurde, trotzdem noch die
draw()
Methode aufgerufen wird. Das Objekt sollte zu dem Zeitpunkt jedoch nicht mehr existieren.for (State* state : activeStates) { if (state->isUpdating()) { state->handleEvents(); state->update(); } if (state->isDrawing()) { state->draw(); } }
Allerdings erzeugt
if (state->isDrawing())
keinen Fehler, was komisch ist, weil der State Pointer ja nicht mehr existieren sollte. Außerdem wird der Destruktor des States nicht aufgerufen, wie ich sehe. Warum?
-
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Ich glaube der Fehler liegt darin, dass im Mainloop, welcher über alle State Pointer iteriert, nachdem der Pointer deleted und im Vector entfernt wurde, trotzdem noch die draw() Methode aufgerufen wird.
Wie kommst du zu diesem Schluss? Läuft das Löschen und die gezeigte Loop in separaten Threads?
Ansonsten sind deine Fragen nur per Glaskugel zu beantworten (d.h. der Fehler liegt anderswo).
-
@wob Wenn ich diese Zeile auskommentiere, gibt es keinen Fehler.
-
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Ich glaube der Fehler liegt darin, dass im Mainloop, welcher über alle State Pointer iteriert, nachdem der Pointer deleted und im Vector entfernt wurde, trotzdem noch die draw() Methode aufgerufen wird.
Das geht nur dann, wenn der Vector ein anderer ist als der, aus dem gelöscht wurde.
-
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Allerdings erzeugt if (state->isDrawing()) keinen Fehler, was komisch ist, weil der State Pointer ja nicht mehr existieren sollte
UB (undefined behavior) ist eben genau undefiniert.
-
@manni66 Was passiert denn, wenn ich innerhalb eines
for (auto& : states)
ein Element lösche, dieses aber noch im Rest der Schleife verwendet wird? Warum erzeugt das keinen Error? Und wie kann es sein, dass der Destruktor des States nicht aufgerufen wird, wenn der Pointer dazu gelöscht wird? Wenn ich jetzt stattdessen einen Smart Pointer nutzen würde, wie würde das ungefähr aussehen? Und würde das nicht zu dem gleichen UB führen, weil der Smart Pointer ja auch nicht mehr existier?
-
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Was passiert denn, wenn ich innerhalb eines for (auto& : states) ein Element lösche, dieses aber noch im Rest der Schleife verwendet wird?
Das tut man nicht ergibt undefiniertes Verhalten, weil begin und end nur einmalig am Anfang abgefragt werden und du somit über das Ende hinaus loopen würdest.
Aber welche deiner Funktionen isUpdating, isDrawing, handleEvents, update, draw tut das denn? Keine davon sieht so aus, als kennte sie den activeStates-vector und könnte also daraus was löschen.
Und wie kann es sein, dass der Destruktor des States nicht aufgerufen wird, wenn der Pointer dazu gelöscht wird?
Du hast @manni66 s Frage nach dem virtuellen Destruktor noch nicht beantwortet.
Ansonsten: Glaskugel.
-
@manni66 Nein, es gibt keinen virtuellen Destruktor. Und der Main Loop wird in der StateMachine ausgeführt, die den vector besitzt. In der update Funktion irgendeines States ruft dieser die popState() Methode der StateMachine auf und übergibt sich selbst als pointer. Die StateMachine löscht den Pointer dann und entfernt das zugehörige Element aus dem Vector. Dann, wenn update() des States abgeschlossen ist, wird draw() aufgerufen, nur halt mit dem invaliden Pointer. Warum ruft
delete *it
nicht den Destruktor des States auf?
-
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Warum ruft delete *it nicht den Destruktor des States auf?
@daniel sagte in Segmentation fault beim Deleten eines Pointers:
Nein, es gibt keinen virtuellen Destruktor.
-
Das hast du dir doch selbst beantwortet:
@daniel sagte in Segmentation fault beim Deleten eines Pointers:Nein, es gibt keinen virtuellen Destruktor.
So wird nur der Destruktor des Basisklassenobjektes (also
~State()
) aufgerufen.Aber selbst damit darfst du innerhalb der Schleife keine Elemente aus dem Vector löschen.
Du könntest sie als "gelöscht" markieren und nachher dann aus dem Vector löschen (aber auch dort nicht per "for-range"-Schleife!).
-
@Th69 Aber warum sollte ich einen Destruktor für die Basisklasse brauchen? Wenn ich die davon erbende Klasse lösche, wird doch ein Default Konstruktor für die Basisklasse erstellt, oder nicht? Und es wird ja auch nicht der Destruktor der erbenden Klasse aufgerufen.
-
@daniel Dein State soll einen virtuellen Destruktor haben, damit die Klasse weiß, dass sie den Destruktor von der abgeleiteten Klasse aufrufen soll. Es gilt die Regel: Wenn du von einer Klasse erben möchtest, mache den Destruktor virtual!
-
Dann lies dich mal über virtuelle Destruktoren ein, z.B. Destructors ("Virtual destructors") oder Virtual Destructor.
-
Alles klar ich werde mich da mal genauer einlesen. Mit dem virtuellen Destruktor und der validState Flag funktioniert es. Vielen Dank für die Hilfe von allen!