Wie oft suchen Sie in einer neuen C ++ - Anweisung nach einer Ausnahme?

8

Ich habe gerade angefangen Effektives C ++ zu lesen und bin an den Punkt gelangt, an dem der Autor über den Operator new spricht.

Das Buch erklärt sehr gut, wie Sie die Ausnahme std :: bad_alloc (mit verschiedenen Graden an Eleganz) fangen können, die der Operator new auslösen kann, wenn Sie nicht mehr genügend Speicher haben.

Meine Frage ist: Wie oft überprüfen Sie den Fall, wenn nicht genügend Speicher vorhanden ist, um ein Objekt zu instanziieren, wenn überhaupt? und warum? Ist es die Mühe wert?

    
Loris 30.12.2008, 10:35
quelle

10 Antworten

17

Ich erhalte Ausnahmen, wenn ich diese Frage beantworten kann:

  

Was wirst du mit der Ausnahme machen, wenn du sie einmal erwischt hast?

Die meiste Zeit ist meine Antwort: "Ich habe keine Ahnung. Vielleicht weiß mein Anrufer." Also, ich verstehe die Ausnahme nicht. Lasst es jemandem bekannt werden, der es besser weiß.

Wenn Sie eine Ausnahme abfangen und Ihre Funktion weiterlaufen lassen, haben Sie zu Ihrem Programm gesagt: "Macht nichts. Hier ist alles in Ordnung." Wenn du das sagst, bei Gott, ist alles besser. Wenn Ihnen also der Arbeitsspeicher ausgegangen ist, sollten Sie nach der Verarbeitung von std::bad_alloc nicht mehr genügend Arbeitsspeicher haben. Sie sollten nicht einfach einen Fehlercode aus Ihrer Funktion zurückgeben, da der Aufrufer dann explizit nach diesem Fehlercode suchen muss und Sie immer noch nicht genügend Arbeitsspeicher haben. Ihre Behandlung dieser Ausnahme sollte etwas Speicher freigeben. Leeren Sie einige Caches, legen Sie einige Dinge auf die Festplatte usw. fest. Aber wie viele der Funktionen in Ihrem Programm wollen Sie wirklich dafür verantwortlich machen, die Speichernutzung Ihres Programms zu reduzieren?

Wenn Sie das Problem, das die Ausnahme ausgelöst hat, nicht lösen können, behandeln Sie die Ausnahme nicht.

    
Rob Kennedy 30.12.2008, 16:35
quelle
7

Das Problem ist, dass wenn Sie nicht genügend Arbeitsspeicher zur Verfügung haben, Sie im Allgemeinen nicht viel tun können, außer in einen Absturzspeicherauszug zu schreiben und das Programm zu beenden. Es ist daher nutzlos, jedes neue in Ihrem Programm zu überprüfen.

Eine Ausnahme ist, wenn Sie Speicher z. Laden Sie eine Datei, in diesem Fall müssen Sie nur den Benutzer informieren, dass nicht genug Speicher für die angeforderte Operation verfügbar ist.

    
Edouard A. 30.12.2008 10:56
quelle
4

Ich denke, das Wichtigste ist, sich immer der Möglichkeit bewusst zu sein, dass Ihnen nicht genügend Speicher zur Verfügung steht. Entscheiden Sie dann, ob Sie sich interessieren oder nicht. Überlegen Sie, ob Sie für jede Zuteilung versuchen und fangen wollen - das ist eine Menge Ärger. Wählen Sie zwischen gesteigerter Produktivität und einfacherem Code im Vergleich zu einer Anwendung, die den Fall, dass kein Speicher verfügbar ist, problemlos verarbeiten kann. Ich denke, dass beide Gewinne in den richtigen Kontexten äußerst wertvoll sind, also wähle sorgfältig.

Ja, Sie können Ihr Leben einfacher gestalten, indem Sie eine Template-Basisklasse definieren, die einen benutzerdefinierten Operator new und den Operator delete bereitstellt, und einen neuen new-handler festlegen. Dann können Sie das Muster Curiously Recurring Template von dieser Basisklasse ableiten. Ihre abgeleiteten Klassen werden dann ordnungsgemäß mit fehlerhaften Zuordnungen umgehen, aber Sie müssen immer noch daran denken, von dieser Basisklasse für jede neu erstellte Klasse abzuleiten. Oftmals kann es zu Mehrfachvererbung kommen, was zu eigenen Komplexitäten führen kann. Egal, was Sie tun, um schlechte Allokationen zu behandeln, Ihr Code wird nicht so einfach sein, als wenn Sie nicht stören.

Darauf gibt es nie eine Antwort. Es ist eine Wahl, die Sie treffen müssen, abhängig vom Kontext wie immer.

    
wilhelmtell 30.12.2008 10:52
quelle
4

Nie. Ich habe immer gedacht, dass das Standardverhalten (es gibt eine std :: bad_alloc Ausnahme, es ist nicht behandelt, und damit das Programm mit einer Fehlermeldung endet) ist gut, für die Anwendungen, in denen ich gearbeitet habe.

    
Daniel Daranas 30.12.2008 11:08
quelle
2

In einem System, das virtuellen Speicher verwendet, gibt malloc () NULL nicht zurück, und new wird std :: bad_alloc nicht zurückgeben; Sie werden eine virtuelle Adresse zurückgeben. Wenn Sie in die Speicherzone schreiben, auf die diese Adresse zeigt, versucht das System, die virtuelle Adresse einer physikalischen Adresse zuzuordnen. Wenn kein Speicher mehr verfügbar ist, erhalten Sie einen Seitenfehler.

Sie fangen also für std :: bad_alloc, wenn Sie sich auf einem eingebetteten System ohne MMU befinden, und hoffen, dass Sie etwas tun können, um etwas Speicher freizugeben.

    
Nicolas Martyanoff 30.12.2008 11:04
quelle
2

Wenn Sie die Ausnahme nicht behandeln, stürzt Ihr Programm ab, und die Supportanfragen, die Sie erhalten, befinden sich irgendwo zwischen "funktioniert nicht", "stürzt nach dem Zufallsprinzip ab" und "Ich habe meine ganze Arbeit an diesem Tag verloren". Wenn Sie denken, dass das in Ordnung ist, dann ist es die Mühe wirklich nicht wert.

Das Mindeste, was Sie tun können, ist, dem Benutzer zu sagen, dass er wirklich keinen Speicher mehr hat. Geben Sie ihm zumindest einen Hinweis, warum die Anwendung zufällig abstürzt.

Zusätzlich können Sie versuchen, Ergebnisse zu speichern, z. Speichern Sie sie in einer Wiederherstellungsdatei. Das könnte jedoch einfacher sein, bevor Sie das Problem lösen.

Es wäre großartig, wenn Sie so weit gehen könnten, eine Fehlermeldung wie "Sie können dieses Bild nicht einfügen, weil Sie nicht genügend Speicher haben" zu geben. Und du würdest weiterarbeiten, als wäre nichts passiert. Dies würde jedoch bedeuten, dass der gesamte Code hinter einem Benutzerbefehl transaktional sein muss und eine starke Ausnahmesicherheitsgarantie geben muss.

Identifizieren Sie also die Kosten für das zufällige Auslaufen von Arbeitsspeicher. Ermitteln Sie auf dieser Grundlage, welches "Schutzniveau" Sie Ihrem Benutzer geben müssen.

    
peterchen 30.12.2008 11:18
quelle
2

Ich denke, das hängt weitgehend von der Art der Anwendungen ab, die Sie schreiben. Wenn ich etwas schreiben würde, das die globale Stabilität des Systems nicht beeinflusst, sagen wir mal ein Spiel oder ein Film-Player, würde ich nicht nach dieser Ausnahme suchen. Meine Anwendung würde std::terminate aufrufen und ich könnte sie irgendwo aufzeichnen, oder mein Kernel würde zuerst mein Programm beenden, um Platz für andere Programme zu schaffen.

Wenn ich ein Programm schreibe, dessen Stabilität direkt mit der des Systems übereinstimmt, auf dem es läuft, sagen wir ein Videotreiber oder ein Init-System, würde ich ständig nach Speicherausnahmen suchen (wahrscheinlich um Zuweisungen in eine Funktion zu legen), und im Falle eines Zuteilungsfehlers Speicher aus einem im Voraus zugewiesenen Pool abrufen.

Ich denke, das hängt von der Verhältnismäßigkeit ab. Was gewinnen Sie von einem erstaunlich stabilen Movie-Player, wenn er aufgrund seiner aggressiven Überprüfung langsamer wird, um Filme abzuspielen?

Übrigens, jemand hat geantwortet, dass malloc 0 nicht zurückgibt, wenn Sie nicht genügend Speicher für einige Systeme haben. Das stimmt, aber wie die Manpage von malloc zeigt (linux specific)

  

Wenn Linux unter Umständen eingesetzt wird, wo es weniger wünschenswert wäre, einige zufällig ausgewählte Prozesse plötzlich zu verlieren, und außerdem die Kernel-Version ausreichend aktuell ist, kann man dieses überbrückende Verhalten mit einem Befehl abschalten: $ echo 2 > /proc/sys/vm/overcommit_memory

     

Siehe auch das Kernel-Dokumentationsverzeichnis, die Dateien vm / overcommit-accounting und sysctl / vm.txt.

    
Johannes Schaub - litb 30.12.2008 11:37
quelle
0

Wenn Sie Speicher für z.B. ein Pfadpuffer, wo Sie wissen, dass es nur ein paar Bytes sein wird, die den Aufwand nicht wert sind.

Aber wenn Sie Speicherplatz für größere Objekte wie Bilder oder Dateien reservieren müssen, sollten Sie die Überprüfung definitiv durchführen.

    
Stefan 30.12.2008 10:41
quelle
0

Es lohnt sich normalerweise nicht, es sei denn, Sie verwenden etwas wie das RAII-Muster (Ressourcenerfassungsinitialisierung). In diesem Fall werden Sie wahrscheinlich remote Ressourcen im Konstruktor zuweisen, was die Verwendung von new zum Erstellen eines großen Puffers beinhalten kann.

In diesem Fall ist es wahrscheinlich besser, die Ausnahme abzufangen, als Sie sich in einem Konstruktor befinden. Da dies in RAII ist, ist es wahrscheinlich, dass nur die Ressource zu viel Speicher benötigt, wodurch Sie dem Benutzer aussagekräftigere Fehlermeldungen zur Verfügung stellen können.

    
ReaperUnreal 31.12.2008 16:05
quelle
0

Es war einmal so, dass Ihr Programm vor dem Zuweisen des letzten möglichen Bytes durch den Swap-Death-Weg gestorben ist, wobei die Festplatte auf eine Auslagerungsdatei mit der Größe Ihres Adressraums zugegriffen hat. Aber mit modernen Systemen, die 4GB + noch laufende 32-Bit-Prozesse halten, ist dieses Verhalten viel seltener. Selbst die größten Prozesse erhalten möglicherweise den gesamten physischen Arbeitsspeicher, den sie verarbeiten können. In diesen Fällen können sie nicht genügend Arbeitsspeicher haben, bevor die Festplatte stirbt.

Es gibt jedoch keine allgemeine Handhabungsstrategie. Jeder Prozess, der Caches implementiert hat, sollte diese leeren - aber ein guter Cache wäre bereits geleert worden, als das Betriebssystem einen niedrigen Speicherzustand signalisiert hat. Ein Prozess, der auf Benutzeranforderungen reagiert, kann bad_alloc bei der Granularität der Benutzeranforderung verarbeiten. Wenn Sie nicht genügend Arbeitsspeicher für die Benutzeraktion haben, ist es im Allgemeinen wenig sinnvoll, den Arbeitsspeicher zu reservieren. Kehren Sie stattdessen zu dem Status vor der Benutzeraktion zurück. Ein nicht-interaktiver Prozess könnte andererseits von einem O (N) zu einem langsameren O (N log N) Algorithmus wechseln.

    
MSalters 02.01.2009 14:58
quelle

Tags und Links