Frage Wie Speicher in Try-Catch-Blöcken freigeben?


Ich habe hoffentlich eine einfache Frage - wie kann man Speicher freigeben, der im try-Block zugewiesen wurde, wenn die Ausnahme auftritt? Betrachten Sie den folgenden Code:

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 Speicher freigeben, nachdem der Heap zugewiesen wurde und vor dem Aufruf eine Ausnahme aufgetreten ist? delete[] heap? Gibt es eine Regel, in diesen try .. catch-Blöcken Speicher auf Heap nicht zuzuweisen?


22
2018-06-15 19:31


Ursprung


Antworten:


Studiere die RAII Idiom (Ressourcenerfassung ist Initialisierung)! Siehe z.B. das Wikipedia Artikel über RAII.

RAII ist nur die allgemeine Idee. Es wird z.B. in der C ++ Standardbibliothek std::unique_ptr oder std::shared_ptr Vorlagenklassen.


Sehr kurze Erklärung des RAII-Idioms:

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

Es funktioniert so:

  • Sie schreiben eine Wrapper-Klasse um Ihre Ressource (z. B. Speicher). Der Destruktor ist verantwortlich für die Freigabe der Ressource.

  • Sie erstellen als lokale (automatische) Variable eine Instanz Ihrer Wrapper-Klasse in einem Bereich. Sobald die Programmausführung diesen Bereich verlässt, wird der Destruktor des Objekts aufgerufen, wodurch die Ressource (z. B. Speicher) freigegeben wird.

Der wichtige Punkt ist das ist egal Wie Der Bereich wird verlassen. Selbst wenn eine Ausnahme ausgelöst wird, wird der Bereich immer noch verlassen und der Destruktor des Wrapper-Objekts wird immer noch aufgerufen.


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`!
}

32
2018-06-15 19:32



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?
}

9
2018-06-15 19:46



Bewegen Sie entweder die new Vor dem try, so dass der Zeiger immer noch im Bereich ist, oder verwenden Sie einen intelligenten Zeiger wie shared_ptr oder unique_ptr (im Notfall, auto_ptr, aber es hat Probleme), die für Sie beim Beenden aufräumen wird. Ausnahmen sind ein wichtiger Grund, warum Smart Pointer wichtig sind.


6
2018-06-15 19:34



Die allgemeine Antwort ist Verwendung RAII.

Es ist jedoch möglich, es zu lösen, indem Sie die Variable aus dem Bereich try {} entfernen:

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 dies nicht als eine gute Praxis empfehle - sondern eher als eine Art "Down & Dreck", die nur verwendet werden kann, wenn Sie die Risiken wirklich kennen und immer noch bereit sind, sie einzugehen. Persönlich würde ich RAII verwenden.

Frieden


6
2018-06-15 20:38



Die richtige Antwort ist RAII und shared_ptr wie oben erwähnt, aber nur um 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, außer dass es Speicher auf dem Stack anstatt auf dem Heap reserviert, also unabhängig davon, wie Sie die Funktion beenden (werfen oder jetzt), wird der Speicher wiederhergestellt, und es sind keine Löschungen oder Freigaben erforderlich.


3
2018-06-15 19:55



Ich muss allen denen zustimmen, die RAII gesagt haben, aber ich würde Boost benutzen shared_array anstelle eines auto_ptr. Automatische Zeigeraufrufe delete und nicht 'delete []', was zu Lecks in einem Array führt.


1
2018-06-15 19:56



Der einfachste Weg wäre, die Variable vor dem try-Block zu deklarieren und dann die Initialisierung innerhalb des Blocks durchzuführen.


0
2018-06-15 19:35



Ja - wenn Sie die Einfachheit in Betracht ziehen - der Zeiger, der sich außerhalb Ihres Testblocks befindet, ist die Lösung.

Grüße


0
2018-06-23 14:24



Stimmte mit den Antworten auf RAII und Smart-Zeigern überein.

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

try { dangerous operations } 
catch { cleanup; throw; }

-4
2018-06-15 19:37