Wer hat bad_alloc geworfen?
-
class A{ int* a; public: A(unsigned int size) throw(bad_alloc) { a=new int[size]; } ~A(){ delete[] a; } }; int main(){ try{ A a=new A(4811); delete a; } catch(bad_alloc &ba){ //von wem stammt die Exception? } return 0; }
Was passiert wenn kein Spiecher mehr vorhanden ist?
Bzw. in welcher reihenfolge werden die news überhaupt aufgerufen?
Kann man davon ausgehen, dass nach Programmende immer der gesamte Speicher wieder korrekt freigegeben wurde?
-
Bzw. in welcher reihenfolge werden die news überhaupt aufgerufen?
der Konstruktor von A wird natürlich erst dann aufgerufen, wenn der Speicher in dem das Objekt angelegt werden soll allokiert wurde.
Kann man davon ausgehen, dass nach Programmende immer der gesamte Speicher wieder korrekt freigegeben wurde?
Nein. Wobei übliche Betriebssysteme das machen sollten. Dennoch sollte man dafür sorgen, dass der Code den Speicher freigibt, der allokiert wurde.
-
Wie könnte ich dann im catch-Zweig unterscheiden, ob ich auch A löschen muss? Vorher mit 0 initialisiren oder nothrow new? Was wäre in diesem Fall das beste/schönste?
kingruedi schrieb:
Wobei übliche Betriebssysteme das machen sollten
Darum geht es mir nicht.
-
D@niel $chumann schrieb:
Was wäre in diesem Fall das beste/schönste?
Vielleicht sind ja Smart Pointer was für dich, oder gleich einen entsprechenden Container verwenden.
-
D@niel $chumann schrieb:
Wie könnte ich dann im catch-Zweig unterscheiden, ob ich auch A löschen muss? Vorher mit 0 initialisiren oder nothrow new? Was wäre in diesem Fall das beste/schönste?
In deinem Beispiel musst du A niemals im catch-Block löschen.
Folgende Szenarios sind möglich:
1. Das new A(...) schlägt fehl: in diesem Fall wurde kein A-Objekt erzeugt und kein Speicher belegt. Ergo musst du auch nichts freigeben.2. Das new im Ctor von A schlägt fehl: in diesem Fall wurde kein A-Objekt erzeugt (es wurde aber bereits Speicher allokiert). Es wird kein Destruktor aufgerufen. In *diesem* Fall kein Problem, da für dein int-Array ja kein Speicher angefordert wurde.
Der C++ Standard garantiert dir aber gleichzeitig, dass der zuvor für das A-Objekt angelegte Speicher automatisch freigegeben wird (sofern du keine unklugen Spielchen bei der Überladung von klassenspezifischen new/delete-Operator-Funktionen betrieben hast).3. Beide Allokationen sind erfolgreich: somit wurde ein A-Objekt erzeugt. Da zwischen der Konstruktion des A-Objekts und dem delete a; keine weitere Anweisung steht kann keine weitere Exception geworfen werden. Ergo wird alles durch das delete alles korrekt freigegeben.
Problematisch wird es, wenn A zwei oder mehr Ressourcen anfordert.
class A{ int* a; int* b; public: A(unsigned int size) throw(bad_alloc) { a=new int[size]; b = new int(2); } ~A(){ delete b; delete[] a; } };
Jetzt hättest du im Zweifelsfall ein Memory-Leak und zwar genau dann, wenn die Allokation des Arrays erfolgreich abläuft, dann bei der Allokation von b aber eine Exception geworfen wird.
In diesem Fall wird der für das A-Objekt allokierte Speicher zwar freigegeben, nicht aber der für das Array. Da kein A-Objekt konstruiert wurde, wird auch kein Dtor aufgerufen.Die Lösung des Problems ist ganz simpel. Verwalte jede dynamische Ressource über ein Manager-Objekt und gib die Ressource in dessen Dtor frei.
-
Ah, vielen Dank, genau das wollte ich wissen.
HumeSikkins schrieb:
Der C++ Standard garantiert dir aber gleichzeitig, dass der zuvor für das A-Objekt angelegte Speicher automatisch freigegeben wird
also doch, so habe ich mir das auch gedacht/erhofft.
Die Idee mit einer Klasse für jede dynamische Ressource find ich auch gut, lohnt sich aber bei meiner kleinen Klasse IMHO nicht, momentan hab ich da einfach einen kleinen try-catch-Block, in dem ich die vorher angeforderte Resource freigebe und die exception erneut werfe.
[Seitdem ich meine Projekte in C++ schreibe, anstatt in C, bin ich soweiso nur noch am Klassen schreiben (ohne sie endlich mal zu verwenden) und am überlegen, ob mein Design wirklich richtig ist. Naja, so langsam wirds]