Wie deklariere ich std :: unique_ptr und wozu dient es?

91

Ich versuche zu verstehen, wie es std::unique_ptrfunktioniert und dafür habe ich dieses Dokument 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.

römisch
quelle
12
new int(3)Gibt einen Zeiger auf das Neue zurück int, genau wie pdein Zeiger auf das Neue double.
David Schwartz

Antworten:

86

Der Konstruktor von unique_ptr<T>akzeptiert einen Rohzeiger auf ein Objekt vom Typ T(also akzeptiert er a T*).

Im ersten Beispiel:

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

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

unique_ptr<double> uptr2 (pd);

Der Zeiger wird in der pdVariablen gespeichert .

Konzeptionell ändert sich nichts (Sie konstruieren einen unique_ptraus einem Rohzeiger), aber der zweite Ansatz ist möglicherweise gefährlicher, da Sie beispielsweise Folgendes tun könnten:

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

Somit gibt es zwei eindeutige Zeiger, 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 Ihrem Zweifel:

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

Intelligente Zeiger sollen den Objektbesitz modellieren und automatisch dafür sorgen, dass das spitze Objekt zerstört wird, wenn der letzte (intelligente, besitzende) Zeiger auf dieses Objekt außerhalb des Gültigkeitsbereichs liegt.

Auf diese Weise müssen Sie sich nicht daran erinnern, dass Sie deleteObjekte dynamisch zugewiesen haben - 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 nicht auf ein bereits zerstörtes Objekt dereferenzieren:

{
    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_ptrhandelt es sich um einen intelligenten Zeiger, der die eindeutige Eigentümerschaft modelliert. Dies bedeutet, dass zu jedem Zeitpunkt in Ihrem Programm nur ein (besitzender) Zeiger auf das spitze Objekt vorhanden sein darf - daher kann er unique_ptrnicht 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 Speicher verloren geht, und die ordnungsgemäße Eigentumsrichtlinie für Ihr Objekt wird durchgesetzt. Rohe Zeiger geben Ihnen diese Garantie nicht.

Andy Prowl
quelle
3
Hallo, ich konnte nichts darüber verstehen model object ownership, das integer leakim Code oder enforcing ownership policy for object. Könnten Sie bitte Themen / Ressourcen vorschlagen, um diese Konzepte zu lernen?
Flamme von Udun
1
Ich kann nicht verwenden unique_ptr, ohne einen Fehler zu bekommen: The text ">" is unexpected. It may be that this token was intended as a template argument list terminator but the name is not known to be a template.obwohl ich #include <utility>und habe #include <memory>. Irgendein Rat?
Anonym
16

Es gibt keinen Unterschied in der Arbeit in beiden Konzepten der Zuweisung zu unique_ptr.

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

ist ähnlich wie

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

Hier löscht unique_ptr automatisch den von belegten Speicherplatz uptr.


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

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

Im folgenden Code

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

Hier müssen Sie heapInt löschen , wenn es mit fertig ist. Wenn es nicht gelöscht wird, tritt ein Speicherverlust auf.

Um solche Speicherlecks zu vermeiden, wird unique_ptr verwendet, wobei unique_ptr automatisch den von heapInt belegten Speicherplatz löscht, wenn der Gültigkeitsbereich verlassen wird. So müssen Sie nicht löschen oder frei für unique_ptr.

fury.slay
quelle
10

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

In diesem Fall:

unique_ptr<double> uptr2 (pd);

pdwird zerstört, wenn uptr2der Geltungsbereich überschritten wird. Dies erleichtert die Speicherverwaltung durch automatisches Löschen.

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

fatihk
quelle
-1

Von cppreference , einer der std::unique_ptrKonstrukteure ist

explizites unique_ptr (Zeiger p) noexcept;

Ein neues zu erstellen bedeutet std::unique_ptralso, einen Zeiger an seinen Konstruktor zu übergeben.

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

Oder es ist das gleiche wie

int *int_ptr = new int(3);
std::unique_ptr<int> uptr (int_ptr);

Der Unterschied ist, dass Sie nach der Verwendung nicht aufräumen müssen. Wenn Sie nicht verwenden std::unique_ptr(Smart Pointer), müssen Sie es so löschen

delete int_ptr;

Wenn Sie es nicht mehr benötigen, führt dies zu einem Speicherverlust.

Tevada
quelle