C ++ - Ausnahmebehandlung
Ich bekomme eine Wurfausnahme und brauche deine Bewertung
Die Hauptfunktion unten versucht mehrmals, Speicher zuzuweisen und dann die Ausnahme auf der oberen Ebene auszulösen.
#include <iostream>
#include <memory>
struct MiserlinessClass{
char * pMemory;
int memory_len;
const int max_size = 10;
MiserlinessClass(int len){
if (len>max_size){
std::cout<<"What a lavish lifestyle! Get out of my face! \n";
std::bad_alloc exception;
throw exception;
}
pMemory = (char *)malloc(len*sizeof(char));
memory_len = len;
}
};
int main(int argc, char** argv){
int len = (argc==2)? strtol(argv[1],NULL,10): 5;
std::unique_ptr<MiserlinessClass> objPtr;
bool allocated = false;
const int max_cnt = 5;
int cnt = 0;
while (!allocated){
try{
std::cout<<"Trying to allocate "<<len<<" chars...\n";
objPtr.reset(new MiserlinessClass(len));
allocated = true;
} catch (std::bad_alloc &e){
len = len >> 1;
cnt++;
if (cnt==max_cnt){
std::cout<<"I give up \n";
throw e;
}
}
}
std::cout<< "Allocated " << objPtr->memory_len << " chars \n";
return 0;
}
Und hier sind die Ergebnisse von 3 verschiedenen Läufen
1 - weist Speicher vom ersten Versuch an zu,
2 - versucht es einige Male und weist verfügbare und T zu
3 - fehlgeschlagen nach N Versuchen und eine Ausnahme auf die obere Ebene werfen
---------------------------------------
$ make; ./01_exception_pointer 3 make: Nothing to be done for 'all'. Trying to allocate 3 chars... Allocated 3 chars --------------------------------------- $ make; ./01_exception_pointer 64
make: Nothing to be done for 'all'.
Trying to allocate 64 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 32 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 16 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 8 chars...
Allocated 8 chars
---------------------------------------
$ make; ./01_exception_pointer 1023
make: Nothing to be done for 'all'.
Trying to allocate 1023 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 511 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 255 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 127 chars...
What a lavish lifestyle! Get out of my face!
Trying to allocate 63 chars...
What a lavish lifestyle! Get out of my face!
I give up
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted (core dumped)
Meine Fragen sind
Ich kenne den formalen Unterschied zwischen der Rückgabe von Werten und dem Auslösen der Ausnahme, kann aber immer noch nicht spüren, ob es Zeit ist, in Panik zu geraten und eine Ausnahme auszulösen, oder ob das Programm ruhig bleiben und nur den Fehlercode zurückgeben sollte.
Ist ein angemessener Weg, um eine bad_alloc-Ausnahme zu behandeln?
Irgendwelche zusätzlichen Kommentare und Vorschläge :)
Antworten
Ich kenne den formalen Unterschied zwischen der Rückgabe von Werten und dem Auslösen der Ausnahme, kann aber immer noch nicht spüren, ob es Zeit ist, in Panik zu geraten und eine Ausnahme auszulösen, oder ob das Programm ruhig bleiben und nur den Fehlercode zurückgeben sollte.
Einfache Faustregeln.
Wenn Sie das Problem nicht lokal beheben können, lösen Sie eine Ausnahme aus.
Sie können das Problem nicht lösen, dass lokal nicht genügend Speicher vorhanden ist. Setzen Sie daher eine Ausnahme. Auf diese Weise kann der Stapel zum Abwickeln des Speichers freigegeben werden und Sie können einen Punkt erreichen, an dem dies behoben werden kann (oder wenn nicht, lassen Sie die Anwendung beenden). Ein Beispiel dafür, wo es gestoppt werden kann, ist, wenn Sie eine unabhängige Aufgabe erstellen und eine dieser Aufgaben fehlschlägt. Dies bedeutet nicht, dass alle Aufgaben fehlschlagen. Protokollieren Sie die Tatsache, dass diese Aufgabe fehlgeschlagen ist, und lassen Sie die Ausnahme den gesamten verwendeten Speicher freigeben. Versuchen Sie dann die nächste Aufgabe.Fehlercode sollte keine Schnittstellengrenzen überschreiten.
Fehlercodes sind großartig, wenn Sie sie überprüfen. Es ermöglicht einem einfachen Mechanismus, Informationen eine oder zwei Ebenen zurückzugeben, ohne den Code zu komplizieren. Wenn Sie also eine Bibliothek schreiben und intern Fehlercodes verwenden, ist das in Ordnung (weil Sie gut sind und alle Fehlercodes überprüfen). Sie können Benutzern Ihrer Bibliothek jedoch nicht vertrauen. Wenn sich ein Fehler außerhalb Ihrer Bibliothek ausbreitet, verwenden Sie eine Ausnahme, um den Benutzer zu zwingen, ihn explizit abzurufen.Benutzereingaben und Streams. Verwenden Sie keine Ausnahmen. Benutzereingaben sind immer fehleranfällig und der Code, der Benutzereingaben verarbeitet, muss viele Validierungsprüfungen durchlaufen (wenn dies korrekt durchgeführt wird). Sie wissen das. Setzen Sie den Stream bei Stream-Vorgängen einfach auf "schlecht".
Ist ein angemessener Weg, um bad_alloc Ausnahme zu behandeln?
Lassen Sie es an die Spitze Ihrer App übertragen. Protokollieren Sie etwas, um den Benutzer darüber zu informieren, dass es passiert ist. Wenn dies eine unabhängige Aufgabe ist, lassen Sie die Aufgabe fallen und starten Sie die nächste. Wenn dies nur ein normaler Teil der Ausführung ist, lassen Sie die Anwendung beenden.
Irgendwelche zusätzlichen Kommentare und Vorschläge :)
- Verwenden Sie
malloc()/ nichtfree()in C ++ - Code. - Für jedes neue sollte es ein Löschen geben.
- Verwenden Sie
make_unique()lieber alsnew(um beim Neu- / Löschen von Übereinstimmungen zu helfen).
Code-Review:
// Owned pointers are a bad idea.
struct MiserlinessClass{
char * pMemory; // This is an owned pointer.
// At lot of extra work needs to be done here
// You need to look up the rule of three/five
std::bad_alloc exception;
throw exception;
Einfacher zu schreiben:
throw std::bad_alloc;
Dies ist keine bad_alloc-Situation.
if (len>max_size){
std::cout<<"What a lavish lifestyle! Get out of my face! \n";
std::bad_alloc exception;
throw exception;
}
bad_alloc bedeutet, dass das System aufgrund des Speicherdrucks keinen Speicher zuordnen konnte.
Sie sollten Folgendes verwenden: std::range_errorDer Eingabeparameter lag außerhalb des Bereichs.
Verwenden Sie keine mallocVerwendung newhier.
pMemory = (char *)malloc(len*sizeof(char));
// Better:
pMemory = new char[len];
Hinweis: Hier müssen Sie noch die Dreierregel implementieren.
Hinweis: Ja, Sie sollten oben eine Ausnahme auslösen. Sie möchten dem Benutzer nicht erlauben, ungültige Objekte zu erstellen. Dies muss behoben werden, bevor die Anwendung in die Produktion aufgenommen werden kann. Daher ist es die richtige Lösung, den Komponententest mit einer Ausnahme zum Fehlschlagen zu zwingen.
Es gibt keinen Destruktor für die Klasse MiserlinessClass. Der mit zugewiesene Speicher wird malloc()also verloren gehen. Sie müssen schneller keinen Speicher mehr haben.
Siehe Dreierregel.
Hinweis: Dadurch wird das vorherige Objekt freigegeben. Aber nur, wenn das neue Objekt erfolgreich erstellt wurde. Sie haben also eine Menge Speicher zugewiesen. Dann versuchen Sie, doppelt so viel zuzuweisen. Wenn diese Zuordnung funktioniert, geben Sie den alten Speicher frei.
objPtr.reset(new MiserlinessClass(len));
Ich würde die Erklärung verschieben:
std::unique_ptr<MiserlinessClass> objPtr;
in den try-Block die Zuordnung dort vornehmen.
try{
std::cout<<"Trying to allocate "<<len<<" chars...\n";
std::unique_ptr<MiserlinessClass> objPtr = std::make_unique<MiserlinessClass>(len);
allocated = true;
} catch (std::bad_alloc &e){
...
Jetzt. Der Speicher wird zugewiesen und am Ende des Versuchs freigegeben. Wenn Sie das nächste Mal die Schleife durchlaufen, wissen Sie, dass Sie einen sauberen Speicher zum Zuweisen haben, da bei Ihrem letzten Versuch alles bereinigt wurde.
Sie könnten noch einen Schritt weiter gehen und einfach das entfernen unique_ptr.
try{
std::cout<<"Trying to allocate "<<len<<" chars...\n";
MiserlinessClass objPtr(len);
allocated = true;
} catch (std::bad_alloc &e){
...
Warum nicht einfach mit 2 multiplizieren?
len = len >> 1;
Oder ist das Teilen durch 2. In beiden Fällen ist die Absicht nicht klar. Verwenden Sie Code, der Ihre Absicht klar zum Ausdruck bringt.
Druckt nur, wenn keine Ausnahme vorliegt.
std::cout<< "Allocated " << objPtr->memory_len << " chars \n";