Frage

Ich habe eine einfache Frage hoffentlich - wie funktioniert ein freier Speicher, die im try-Block zugeordnet wurde, wenn die Ausnahme auftritt? Betrachten Sie den folgenden Code ein:

try
 {
  char *heap = new char [50];
        //let exception occur here
  delete[] heap;
 }
 catch (...)
 {
  cout << "Error, leaving function now";
  //delete[] heap; doesn't work of course, heap is unknown to compiler
  return 1;
 }

Wie kann ich freier Speicher nach dem Heap zugewiesen wurde und Ausnahme aufgetreten ist, bevor delete[] heap Aufruf? Gibt es eine Regel keinen Speicher auf Heap in diesen try .. catch-Blöcke zugewiesen werden?

War es hilfreich?

Lösung

Studium der RAII Idiom ( Resource Acquisition Is Initialization )! Siehe z.B. Wikipedia-Artikel über RAII .

RAH ist nur die allgemeine Idee. Es wird eingesetzt, z.B. in der C ++ Standardbibliothek ist std::unique_ptr oder std::shared_ptr Template-Klassen.


Sehr kurze Erklärung des RAII Idioms:

Im Grunde ist es die C ++ Version von try..finally Blöcken in einigen anderen Sprachen. Das RAII Idiom ist wohl flexibler.

Es funktioniert wie folgt:

  • Sie schreiben eine Wrapper-Klasse um Ihre Ressource (zum Beispiel Speicher). Der destructor ist verantwortlich für die Ressource zu befreien.

  • Sie erstellen, als lokalen (automatischer) Variable, eine Instanz der Wrapper-Klasse in einem Rahmen. Sobald die Programmausführung Blätter dass Rahmen, wird das Objekt des destructor, aufgerufen werden, wodurch die Ressourcenfreigabe (z.B. Speicher).

Der wichtige Punkt ist , dass es keine Rolle, wie der Umfang verlassen wird. Selbst wenn eine Ausnahme ausgelöst wird, wird der Umfang noch verlassen und der Destruktor der Wrapper-Objekt ist immer noch genannt wird.


Sehr grobes Beispiel:

// BEWARE: this is NOT a good implementation at all, but is supposed to
// give you a general idea of how RAII is supposed to work:
template <typename T>
class wrapper_around
{
  public:
    wrapper_around(T value)
        : _value(value)
    { }
    T operator *()
    {
        return _value;
    }
    virtual ~wrapper_around()
    {
        delete _value;  // <-- NOTE: this is incorrect in this particular case;
                        // if T is an array type, delete[] ought to be used
    }
  private:
    T _value;
};
// ...

{
    wrapper_around<char*> heap( new char[50] );
    // ... do something ...

    // no matter how the { } scope in which heap is defined is exited,
    // if heap has a destructor, it will get called when the scope is left.
    // Therefore, delegate the responsibility of managing your allocated
    // memory to the `wrapper_around` template class.
    // there are already existing implementations that are much better
    // than the above, e.g. `std::unique_ptr` and `std::shared_ptr`!
}

Andere Tipps

OK Herr Java-Programmierer:

try
{
    // Exception safe dynamic allocation of a block of memory.
    std::vector<char>  heap(50);

    // DO STUFF

    // Note in C++ we use stack based objects and their constructor/destructor
    // TO give a deterministic cleanup, even in the presence of exceptions.
    //
    // Look up RAII (bad name for a fantastic concept).
}
catch (...)
{
    cout << "Error, leaving function now";
    return 1;  // Though why you want to return when you have not fixed the exception is
               // slightly strange. Did you want to rethrow?
}

Die allgemeine Antwort ist die Verwendung RAII.

Allerdings, ist es möglich, sie zu lösen, indem Sie die Variable aus dem Versuch zu bewegen {} -umfang:

char * heap = NULL;
try {
  heap = new char [50];
  ... stuff ...
} catch (...) {
  if (heap) {
    delete[] heap;
    heap = NULL;
  }
  ... however you want to handle the exception: rethrow, return, etc ...
}

Bitte beachten Sie, dass ich nicht dies als eine gute Praxis zu empfehlen - aber eher ein Down & Dirty nur verwendet werden, wenn Sie wirklich die Risiken kennen und sind immer noch bereit, sie zu nehmen. Persönlich würde ich RAII verwenden.

Frieden

Sie entweder die new vor dem try bewegen, so dass der Zeiger noch im Gültigkeitsbereich befindet, oder einen Smart-Pointer wie shared_ptr oder unique_ptr verwenden (in einer Prise, auto_ptr, aber es hat Probleme), die für Sie beim Verlassen aufzuräumen wird. Ausnahmen sind ein großer Grund, warum intelligente Zeiger sind wichtig.

Die ‚richtige‘ Antwort ist RAII und shared_ptr wie oben erwähnt, sondern nur vollständig zu sein: in Ihrem Beispiel, könnten Sie ersetzen

char *heap = new char [50];

mit

char *stack = static_cast<char*>( alloca(50) );

alloca ist fast identisch mit malloc, mit der Ausnahme, dass es Alocs Speicher auf dem Stapel anstelle des Haufens, also egal, wie Sie Ausfahrten funktionieren (werfen oder jetzt), wird der Speicher zurückgewonnen werden, und keine Löschungen oder gibt sind notwendig.

Ich habe mit allen jenen zustimmen, daß die RAH jedoch würde ich Boost-der shared_array anstelle eines auto_ptr. Auto Zeiger ruft delete und nicht 'delete []', die Lecks mit einem Array führen wird.

Der einfachste Weg wäre, die Variable vor dem try-Block zu erklären, und dann tut nur die Initialisierung innerhalb des Blockes.

Ja - wenn Sie die Einfachheit erwägen -. Zeiger, der auf Ihren try-Block Außen ist die Lösung

Viele Grüße

Einverstanden mit den Antworten auf RAII und intelligente Zeiger.

Wenn Sie jedoch darauf bestehen, Sie können dies tun:

try { dangerous operations } 
catch { cleanup; throw; }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top