Die Aufgabe von GotW # 8 besteht darin, eine ausnahmeutrale generische Stack-Datenstruktur in C ++ zu implementieren Nur der Destruktor des Template-Arguments wirft nicht. Der Trick besteht darin, potentiell auftretende Template-Argument-Operationen (Konstruktor, Kopierkonstruktor, Zuweisung) so zu behandeln, dass der Stack in einem konsistenten Zustand bleibt, wenn sie geworfen werden.
In der Lösung sagt Herb Sutter
Um diese Lösung einfacher zu halten, habe ich mich entschieden, die Basisklasse-Technik für ausnahmesicheres Ressourceneigentum nicht zu demonstrieren.
Nachdem ich gegoogelt hatte, fand ich diese Antwort von Dave Abrahams stammt aus dem Jahr 1997. In seiner Lösung behandelt er die Zuordnung und Löschung des Speichers in der Basisklasse und implementiert die Stapeloperationen in der Unterklasse. Auf diese Weise stellt er sicher, dass das Kopieren von Elementen im Kopierkonstruktor von der Speicherzuweisung getrennt ist. Wenn das Kopieren fehlschlägt, wird der Basisklassendestruktor aufgerufen, egal was passiert.
Als Referenz, hier ist Daves Kopierkonstruktor mit meinem Kommentar hinzugefügt:
%Vor%Wenn der Basiskonstruktor erfolgreich ist, ist die Speicherbereinigung im Basisdestruktor garantiert, selbst wenn der Kopierkonstruktor der Unterklasse auslöst.
Meine Fragen sind:
Ich habe diesen Kopierkonstruktor entwickelt, als ich das Problem selbst gelöst habe:
%Vor% Ich finde es etwas umständlich, eine Basisklasse zu haben, wenn man mit diesem Ansatz vergleicht. Gibt es einen Grund, warum die Basisklassentechnik gegenüber dieser temp-copy-then-swap-Methode bevorzugt werden sollte? Beachten Sie, dass sowohl Dave als auch ich das swap()
Mitglied haben, weil wir es in unserem operator=()
verwenden.
Anmerkungen:
Push()
in einer Schleife meiner Verwendung von std::copy
entspricht
Im Verhalten sind die beiden Implementierungen gleich. Sie richten beide ein verwaltetes Speicherzuordnungsobjekt ein, das beim Beenden des Bereichs bereinigt wird, wenn der Konstruktor fehlschlägt. Das Kopieren auf eine temporäre Variable könnte teurer sein, aber, wie in den Kommentaren erwähnt, würde std::move
wahrscheinlich diese zusätzlichen Kosten zunichte machen. Als Antwort auf Ihre spezifischen Fragen:
StackBase<T>
verwenden, können sie sicher annehmen, dass ihr dynamischer Speicher bereinigt wird, wenn sie eine Ausnahme auslösen. In Ihrer Implementierung müssten Sie das temporäre Objekt und den Auslagerungscode (zumindest Teile davon) neu schreiben, um dasselbe zu erreichen. Effektiv verringert diese Implementierung die Anzahl von Zeilen, um mehrere Unterklassen von StackBase zu implementieren. Bei der Implementierung wird jedoch die mehrfache Vererbung vermieden, wenn Sie eine zusätzliche Speicherzuweisung auf einer anderen Basisklasse wünschen. Ihr Code vermeidet auch die Zeit und die Größen für das Blähieren von Template-Code-Blossungen - obwohl ich dies in den meisten Fällen nicht für sehr negativ halte. Ich würde wahrscheinlich etwas verwenden, das Ihrem Code sehr nahe kommt, es sei denn, ich habe versucht, einen sehr allgemeinen Anwendungsfallcode zu schreiben. Tags und Links c++