Stroustrup hat dies auf der Going Native-Konferenz 2013 einige gute Kommentare abgegeben.
Springe in diesem Video einfach zu ungefähr 25m50s . (Ich würde empfehlen, das gesamte Video anzuschauen, aber dies geht zu den Informationen über die Speicherbereinigung über.)
Wenn Sie eine wirklich großartige Sprache haben, die es einfach (und sicher, vorhersehbar, leicht zu lesen und leicht zu lehren) macht, direkt mit Objekten und Werten umzugehen, und die (explizite) Verwendung der Sprache vermeidet Haufen, dann brauchen Sie nicht einmal wollen Garbage collection.
Mit modernem C ++ und dem, was wir in C ++ 11 haben, ist die Speicherbereinigung nur unter bestimmten Umständen mehr wünschenswert. Selbst wenn ein guter Garbage Collector in einen der wichtigsten C ++ - Compiler integriert ist, wird er meiner Meinung nach nicht sehr oft verwendet. Es wird einfacher und nicht schwieriger sein, den GC zu vermeiden.
Er zeigt dieses Beispiel:
void f(int n, int x) {
Gadget *p = new Gadget{n};
if(x<100) throw SomeException{};
if(x<200) return;
delete p;
}
Dies ist in C ++ nicht sicher. Aber es ist auch in Java unsicher! Wenn die Funktion in C ++ vorzeitig zurückkehrt, delete
wird sie niemals aufgerufen. Wenn Sie jedoch über eine vollständige Speicherbereinigung verfügen, z. B. in Java, erhalten Sie lediglich den Vorschlag, dass das Objekt "irgendwann in der Zukunft" zerstört wird ( Update: Es ist noch schlimmer, dass dies der Fall ist. Java tut dies nichtversprechen, den Finalizer jemals anzurufen - er wird vielleicht nie aufgerufen). Dies ist nicht gut genug, wenn Gadget ein offenes Dateihandle oder eine Verbindung zu einer Datenbank oder Daten enthält, die Sie zum späteren Schreiben in eine Datenbank gepuffert haben. Wir möchten, dass das Gadget zerstört wird, sobald es fertig ist, um diese Ressourcen so schnell wie möglich freizugeben. Sie möchten nicht, dass Ihr Datenbankserver mit Tausenden von Datenbankverbindungen zu kämpfen hat, die nicht mehr benötigt werden. Er weiß nicht, dass Ihr Programm nicht mehr funktioniert.
Was ist die Lösung? Es gibt einige Ansätze. Der offensichtliche Ansatz, den Sie für die überwiegende Mehrheit Ihrer Objekte verwenden, ist:
void f(int n, int x) {
Gadget p = {n}; // Just leave it on the stack (where it belongs!)
if(x<100) throw SomeException{};
if(x<200) return;
}
Die Eingabe erfordert weniger Zeichen. Es muss nicht new
im Weg stehen. Sie müssen nicht Gadget
zweimal eingeben. Das Objekt wird am Ende der Funktion zerstört. Wenn Sie dies möchten, ist dies sehr intuitiv. Gadget
s verhalten sich genauso wie int
oder double
. Vorhersehbar, leicht zu lesen, leicht zu lehren. Alles ist ein "Wert". Manchmal ein großer Wert, aber Werte sind einfacher zu vermitteln, weil Sie diese "Aktion auf Distanz" nicht haben, die Sie mit Zeigern (oder Referenzen) erhalten.
Die meisten von Ihnen erstellten Objekte sind nur für die Funktion vorgesehen, mit der sie erstellt wurden, und werden möglicherweise als Eingaben an untergeordnete Funktionen übergeben. Der Programmierer sollte nicht an die Speicherverwaltung denken müssen, wenn er Objekte zurückgibt oder Objekte auf andere Weise für weit voneinander entfernte Teile der Software freigibt.
Umfang und Lebensdauer sind wichtig. Meistens ist es einfacher, wenn die Lebensdauer dem Umfang entspricht. Es ist leichter zu verstehen und leichter zu lehren. Wenn Sie eine andere Lebensdauer wünschen, sollte es offensichtlich sein, dass Sie den Code lesen, den Sie tun, indem Sie shared_ptr
beispielsweise verwenden. (Oder (große) Objekte nach Wert zurückgeben, Bewegungssemantik nutzen oder unique_ptr
.
Dies scheint ein Effizienzproblem zu sein. Was ist, wenn ich ein Gadget zurückgeben möchte foo()
? Die Verschiebungssemantik von C ++ 11 erleichtert die Rückgabe großer Objekte. Schreiben Gadget foo() { ... }
Sie einfach und es wird einfach funktionieren und schnell funktionieren. Sie müssen sich nicht mit sich &&
selbst anlegen, sondern nur die Dinge nach Wert zurückgeben, und die Sprache kann häufig die erforderlichen Optimierungen vornehmen. (Schon vor C ++ 03 haben Compiler bemerkenswert gute Arbeit geleistet, um unnötiges Kopieren zu vermeiden.)
Wie Stroustrup an anderer Stelle im Video sagte (paraphrasiert): "Nur ein Informatiker würde darauf bestehen, ein Objekt zu kopieren und dann das Original zu zerstören. (Publikum lacht). Warum nicht einfach das Objekt direkt an den neuen Ort bewegen? Das ist, was Menschen (keine Informatiker) erwarten. "
Wenn Sie garantieren können, dass nur eine Kopie eines Objekts benötigt wird, ist es viel einfacher, die Lebensdauer des Objekts zu verstehen. Sie können auswählen, welche Lebenszeitrichtlinie Sie möchten, und die Speicherbereinigung ist vorhanden, wenn Sie möchten. Wenn Sie jedoch die Vorteile der anderen Ansätze verstehen, werden Sie feststellen, dass die Speicherbereinigung ganz unten in Ihrer Liste der Einstellungen steht.
Wenn das bei Ihnen nicht funktioniert, können Sie es verwenden unique_ptr
oder fehlschlagen shared_ptr
. Gut geschriebenes C ++ 11 ist kürzer, leichter zu lesen und leichter zu unterrichten als viele andere Sprachen, wenn es um Speicherverwaltung geht.