Frage

Wir haben eine Klasse, deren semantische Verhalten ist wie folgt aus: -

struct Sample
{
  ~Sample() throw() 
  {
    throw 0;
  }
};

void f ()
{
  try
  {
    delete new Sample;
  }
  catch (...){
  }
}

Ich weiß, dass Ausnahmen in dtors werfen ist böse; aber die Aufgabe einer 3rd Party Bibliothek Ressource wirft eine Ausnahme (kann aber wieder accquired sofort, etwas seltsam!). Es gibt auch einen Pool von dieser Ressource, sagt einen Array / Behälter der Klasse Probe. So gibt es zwei Fälle zu betrachten: Zerstörung eines dynamisch zugewiesenen Objekt und Zerstörung einer dynamisch zugewiesenen Array von Objekten.

Zur Zeit der Anwendung abstürzt zufällig bei verschiedenen Ausführungs-Punkte nur, wenn die Array-Version (Pool) verwendet wird. Wir glauben, dass dies zu einer Speicherbeschädigung aufgrund ist aber dann, warum die unpooled Version arbeiten?.

Was passiert mit dem zugewiesenen Speicher? Ist es nicht definiertes Verhalten? Was passiert im Falle eines Arrays? Hat die dtors (atleast, nicht Speicher) alle Elemente eines Arrays (zB wenn der dtor des ersten Elements Würfe) genannt werden?

Vielen Dank im Voraus,

EDIT-1: Nun, wir verfolgten sie bis auf dtors einiger Array-Elemente, die nicht genannt werden. Aber die zugewiesenen Speicher scheint keine Probleme zu haben ... Es folgt Abschnitt 5.3.5.7 von SC22-N-4411.pdf)

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be
called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some
element of the array throws an exception. —end note ]

<\ schnipp>

Sieht aus wie Speicher immer in solchen Fällen aufgehoben wird. Gehe ich recht in der Standard-Interpretation?

War es hilfreich?

Lösung

Es gibt zwei Dinge, die in dieser Situation passieren kann:

  • terminate () aufgerufen
  • undefiniertes Verhalten

In keinem Fall kann dynamisch zugewiesenen Speicher garantiert wird freigegeben werden (außer dass Programmbeendigung wird natürlich alle Ressourcen auf das O zurück).

Andere Tipps

C ++ endet Ihre Anwendung, wenn ein dtor eine Ausnahme auslöst, während der Stapel wegen einer anderen Ausnahme abgewickelt wird.

Da es praktisch unmöglich ist, unter welchen Umständen ein dtor genannt wird, ist die Standardregel auf nie werfen Ausnahmen von dtors.

Wenn Ihre 3rd Party Bibliothek eine Ausnahme wirft, fängt es in Ihrem dtor, melden Sie es, oder speichert es ist Zustand bis zu einem gewissen statischen Cache, wo Sie es abholen können bis „später“, aber lassen Sie es nicht aus zu entkommen Ihr dtor.

Tun Sie dies, dann sehen, ob Ihre Sammlung von Objekten arbeiten, könnte es Ihre Abstürze verursachen.

UPDATE

Leider bin ich kein spec Anwalt, lieber der Fischers Freund Ansatz "saugen sie einen sehen."

Ich würde eine kleine App mit einer Klasse schreiben, die eine meg aus dem Heap reserviert. In einer Schleife, ein Array der Klassen machen, um die Klassen haben dtor eine Ausnahme aus, und einen Haken, eine Ausnahme am Ende der Schleife throw (den Stapel veranlassen, den dtors des Arrays von Klassen zu entspannen und ruft) und betrachten sehen zu Ihrer VM-Auslastung durch das Dach (was ich bin ziemlich sicher, es wird).

Leider kann ich Ihnen nicht Kapitel und Vers, aber das ist mein „Glaube“

Da Sie in einem Kommentar für Kapitel und Vers gefragt:

15.2: 3 hat eine Notiz und sagte:

„Wenn ein destructor während Stack Abwickeln Ausfahrten mit einer Ausnahme genannt terminate aufgerufen wird (15.5.1). So Destruktoren Regel Ausnahmen fangen sollte und nicht zulassen, sie aus dem destructor propagieren“

Soweit ich erkennen kann, ist die einzige Rechtfertigung für die Behauptung „in der Regel“ ist, dass es möglich ist, sehr sorgfältig, ein Programm zu schreiben, so dass kein Objekt, dessen destructor werfen kann, je als Teil des Stapels Abwickeln gelöscht. Aber das ist ein schwieriger Zustand im Durchschnitt Projekt durchzusetzen, als „Destruktoren dürfen nicht werfen“.

15.5.1 und 2 sagen:

. "In den folgenden Situationen ... - wenn die Zerstörung eines Objekts während Stack Unwinding (15.2) tritt eine Ausnahme mit ... void terminate() heißt"

Es gibt einige andere Bedingungen für terminate () in 15.5.1, die anderen Dinge vorschlagen, die Sie nicht werfen möchten: Kopierkonstruktoren von Ausnahmen, atexit Handler und unexpected. Aber zum Beispiel der wahrscheinlichste Grund für eine Kopie Konstruktor zum Scheitern verurteilt ist aus der Erinnerung, die auf z.B. Linux könnte segfault statt sowieso eine Ausnahme zu werfen. In solchen Situationen terminate () scheint nicht so schlecht.

Sieht aus wie Speicher immer in solchen Fällen aufgehoben wird. Gehe ich recht in der Standard-Interpretation?

Sieht für mich, als ob der Speicher für das Objekt gelöscht wird immer freigegeben. Es folgt nicht, dass jeder Speicher, der es über Zeiger besitzt, und gibt in seiner destructor, freigegeben wird, vor allem wenn es ein Array ist und daher gibt es mehrere Destruktoren zu nennen.

Ach ja, und tun Sie Ihre Drittanbieter-Bibliothek vertraut sein exception-sicher? Ist es möglich, dass die Ausnahme während der freien ist die Bibliothek in einem Zustand verlassen, die ihre Autoren nicht gerechnet haben, und dass der Absturz wegen ist das denn?

1) von destructor eine Ausnahme zu werfen ist schlecht, weil, wenn Ausnahme verarbeitet wird und eine weitere Ausnahme tritt die Anwendung verlassen wird. Also, wenn während der Ausnahme Ihre Anwendung Umgang mit Objekten löscht (z destructor fordert von ihnen auf jedem) und einer der Destruktoren wirft eine weitere Ausnahme beendet die Anwendung.

2) Ich glaube nicht, dass Destruktoren automatisch für den Rest der Elemente im Container aufgerufen werden, wenn einer von ihnen Ausnahme auslöst. Wenn die Ausnahme in Container destructor geworfen wird dann der Rest der Elemente wird auf jedem Fall als Anwendung wird nicht bereinigt den Stapel entspannen, während die Ausnahmebehandlung.

Die Standardmethode destructor des Schreibens sollte wie etwas sein:

A::~A()
{
   try {
         // some cleanup code
   }
   catch (...) {} // Too bad we will never know something went wrong but application will not crash
}

Destructors darf niemals Ausnahmen werfen, um es zu undefinierten Verhalten führt und auch zu Speicherlecks führen könnte. Lassen Sie sich das folgende Beispiel betrachtet

T* p = new T[10];
delete[] p;

Also, wie wird new [] und delete [] reagieren, wenn T eine destructor wirft?

Wir betrachten zunächst, dass die Konstruktionen alle verlief reibungslos und dann während der Lösch [] die vierte oder so destructor wirft. delete [] können wählen, um die Ausnahme zu propagieren, die zu allen anderen T Objekten führen würde, die in der Anordnung verbleiben verloren, nicht auffindbar und daher unzerstörbar. Es kann auch nicht „fangen“ die Ausnahme, denn dann würde löscht nicht mehr exception-neutral sein.

Zweitens sagen einer der Konstrukteure wirft. Sagen Sie den 6. Konstruktor eine Ausnahme auslöst. Während Stapel alle Objekte Abwickeln, die bisher gebaut wurden müssen dekonstruiert werden. So ist der 5., 4., 3. und so weiter destructor bekommen genannt wird. Was passiert, wenn der 4. oder 3. destructor eine weitere Ausnahme auslöst? Sollte es sein absorded oder propagiert?

Es gibt keine Antwort auf diese Frage, so dass dieses Thema führt zu undefiniertem Verhalten.

Und wie in meinem ersten Beispiel skizziert, auch zu Speicherlecks führen könnte ..

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top