it-swarm.com.de

Wie deklariert man std :: unique_ptr und wozu dient es?

Ich versuche zu verstehen, wie std::unique_ptr funktioniert und dafür habe ich this document gefunden. Der Autor geht von folgendem Beispiel aus:

#include <utility>  //declarations of unique_ptr
using std::unique_ptr;
// default construction
unique_ptr<int> up; //creates an empty object
// initialize with an argument
unique_ptr<int> uptr (new int(3));
double *pd= new double;
unique_ptr<double> uptr2 (pd);
// overloaded * and ->
*uptr2 = 23.5;
unique_ptr<std::string> ups (new std::string("hello"));
int len=ups->size();

Was mich verwirrt, ist das in dieser Zeile

unique_ptr<int> uptr (new int(3));

Wir verwenden Integer als Argument (zwischen runden Klammern) und hier

unique_ptr<double> uptr2 (pd);

wir haben einen Zeiger als Argument verwendet. Macht es einen Unterschied?

Was mir auch nicht klar ist, ist, wie sich auf diese Weise deklarierte Zeiger von den auf "normale" Weise deklarierten Zeigern unterscheiden.

71
Roman

Der Konstruktor von unique_ptr<T> akzeptiert einen rohen Zeiger auf ein Objekt vom Typ T (also akzeptiert es ein T*).

Im ersten Beispiel:

unique_ptr<int> uptr (new int(3));

Der Zeiger ist das Ergebnis eines new Ausdrucks, während im zweiten Beispiel:

unique_ptr<double> uptr2 (pd);

Der Zeiger wird in der Variablen pd gespeichert.

Konzeptionell ändert sich nichts (Sie konstruieren ein unique_ptr von einem rohen Zeiger), aber der zweite Ansatz ist möglicherweise gefährlicher, da Sie damit beispielsweise Folgendes ausführen können:

unique_ptr<double> uptr2 (pd);
// ...
unique_ptr<double> uptr3 (pd);

Also mit zwei eindeutigen Zeigern, die dasselbe Objekt effektiv einkapseln (wodurch die Semantik eines eindeutigen Zeigers verletzt wird).

Aus diesem Grund ist das erste Formular zum Erstellen eines eindeutigen Zeigers nach Möglichkeit besser. Beachten Sie, dass wir in C++ 14 Folgendes tun können:

unique_ptr<int> p = make_unique<int>(42);

Welches ist klarer und sicherer. Nun zu deinem Zweifel:

Was mir auch nicht klar ist, ist, wie auf diese Weise deklarierte Zeiger sich von den auf "normale" Weise deklarierten Zeigern unterscheiden.

Intelligente Zeiger sollen den Besitz von Objekten modellieren und sorgen automatisch für die Zerstörung des angezeigten Objekts, wenn der letzte (intelligente, besitzende) Zeiger auf dieses Objekt außerhalb des Gültigkeitsbereichs liegt.

Auf diese Weise müssen Sie nicht daran denken, delete für dynamisch zugewiesene Objekte auszuführen - der Destruktor des intelligenten Zeigers erledigt dies für Sie - und Sie müssen sich auch keine Gedanken darüber machen, ob Sie einen (baumelnden) Zeiger auf ein Objekt dereferenzieren das wurde bereits zerstört:

{
    unique_ptr<int> p = make_unique<int>(42);
    // Going out of scope...
}
// I did not leak my integer here! The destructor of unique_ptr called delete

Jetzt unique_ptr ist ein intelligenter Zeiger, der die eindeutige Eigentümerschaft modelliert. Dies bedeutet, dass zu jedem Zeitpunkt in Ihrem Programm nur ein (besitzender) Zeiger auf das betreffende Objekt vorhanden sein darf - aus diesem Grund unique_ptr kann nicht kopiert werden.

Solange Sie Smart Pointer so verwenden, dass der implizite Vertrag, den Sie einhalten müssen, nicht verletzt wird, haben Sie die Garantie, dass kein Speicherleck auftritt und die ordnungsgemäße Eigentumsrichtlinie für Ihr Objekt durchgesetzt wird. Rohe Zeiger geben Ihnen diese Garantie nicht.

66
Andy Prowl

Es gibt keinen Unterschied zwischen den beiden Konzepten der Zuordnung zu unique_ptr.

int* intPtr = new int(3);
unique_ptr<int> uptr (intPtr);

ist ähnlich wie

unique_ptr<int> uptr (new int(3));

Dabei löscht nique_ptr automatisch den von uptr belegten Platz.


wie auf diese Weise deklarierte Zeiger sich von den auf "normale" Weise deklarierten Zeigern unterscheiden.

Wenn Sie eine Ganzzahl im Heap-Bereich erstellen (mit neuem Schlüsselwort oder malloc ), dann müssen Sie diesen Speicher selbst löschen (mit löschen oder frei bzw.).

Im folgenden Code

int* heapInt = new int(5);//initialize int in heap memory
.
.//use heapInt
.
delete heapInt;

Hier müssen Sie delete heapInt eingeben, wenn dies erledigt ist. Wenn es nicht gelöscht wird, tritt ein Speicherverlust auf.


Um solche Speicherverluste zu vermeiden, wird nique_ptr verwendet, wobei unique_ptr den von heapInt belegten Speicherplatz automatisch löscht, wenn dieser außerhalb des Gültigkeitsbereichs liegt.

8
fury.slay

Es ist garantiert, dass eindeutige Zeiger das von ihnen verwaltete Objekt zerstören, wenn sie den Gültigkeitsbereich verlassen. http://en.cppreference.com/w/cpp/memory/unique_ptr

In diesem Fall:

unique_ptr<double> uptr2 (pd);

pd wird zerstört, wenn uptr2 den Gültigkeitsbereich verlässt. Dies erleichtert die Speicherverwaltung durch automatisches Löschen.

Der Fall von unique_ptr<int> uptr (new int(3)); ist nicht anders, mit der Ausnahme, dass der Rohzeiger hier keiner Variablen zugewiesen ist.

6
fatihk