Wie implementiere ich einen Kopierkonstruktor für eine Klasse mit einer unique_ptr
Mitgliedsvariablen? Ich denke nur an C ++ 11.
c++
c++11
unique-ptr
codefx
quelle
quelle
std::vector
.unique_ptr
. Sie möchten wahrscheinlich einen Verschiebungskonstruktor, wenn Sie die Daten in a ablegen möchtenstd::vector
. Auf der anderen Seite hat der C ++ 11-Standard automatisch Verschiebungskonstruktoren erstellt. Vielleicht möchten Sie also einen Kopierkonstruktor ...Antworten:
Da das
unique_ptr
nicht freigegeben werden kann, müssen Sie entweder seinen Inhalt tief kopieren oder dasunique_ptr
in ein konvertierenshared_ptr
.Sie können, wie NPE erwähnt, einen Verschiebungs-Ctor anstelle eines Kopier-Ctors verwenden, dies würde jedoch zu einer anderen Semantik Ihrer Klasse führen. Ein Move-Ctor müsste das Mitglied explizit beweglich machen über
std::move
:Ein vollständiger Satz der erforderlichen Operatoren führt auch dazu
Wenn Sie Ihre Klasse in a verwenden möchten
std::vector
, müssen Sie grundsätzlich entscheiden, ob der Vektor der eindeutige Eigentümer eines Objekts sein soll. In diesem Fall würde es ausreichen, die Klasse beweglich, aber nicht kopierbar zu machen. Wenn Sie den Kopierer und die Kopierzuweisung weglassen, führt Sie der Compiler durch die Verwendung eines std :: -Vektors mit Nur-Verschieben-Typen.quelle
int
. Wenn Sie eine haben, die eineunique_ptr<Base>
speichertDerived
, wird die oben genannte in Scheiben geschnitten.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
und deleter / Kopierer Informationen.Der übliche Fall für
unique_ptr
eine Klasse ist, dass sie eine Vererbung verwenden kann (andernfalls würde ein einfaches Objekt oft auch funktionieren, siehe RAII). Für diesen Fall gibt es in diesem Thread bisher keine passende Antwort .Hier ist also der Ausgangspunkt:
... und das Ziel ist, wie gesagt,
Foo
kopierbar zu machen .Dazu muss eine tiefe Kopie des enthaltenen Zeigers erstellt werden, um sicherzustellen, dass die abgeleitete Klasse korrekt kopiert wird.
Dies kann durch Hinzufügen des folgenden Codes erreicht werden:
Grundsätzlich gibt es hier zwei Dinge:
Das erste ist das Hinzufügen von Kopier- und Verschiebungskonstruktoren, die implizit gelöscht werden,
Foo
wenn der Kopierkonstruktor vonunique_ptr
gelöscht wird. Der Verschiebungskonstruktor kann einfach durch= default
... hinzugefügt werden , um den Compiler darüber zu informieren, dass der übliche Verschiebungskonstruktor nicht gelöscht werden soll (dies funktioniert, daunique_ptr
bereits ein Verschiebungskonstruktor vorhanden ist, der in diesem Fall verwendet werden kann).Für den Kopierkonstruktor von
Foo
gibt es keinen ähnlichen Mechanismus wie für den Kopierkonstruktor vonunique_ptr
. Man muss also ein neuesunique_ptr
erstellen, es mit einer Kopie des ursprünglichen Pointees füllen und es als Mitglied der kopierten Klasse verwenden.Falls es sich um eine Vererbung handelt, muss die Kopie des ursprünglichen Pointee sorgfältig erstellt werden. Der Grund dafür ist, dass ein einfaches Kopieren über
std::unique_ptr<Base>(*ptr)
im obigen Code zum Schneiden führen würde, dh nur die Basiskomponente des Objekts wird kopiert, während der abgeleitete Teil fehlt.Um dies zu vermeiden, muss das Kopieren über das Klonmuster erfolgen. Die Idee ist, die Kopie über eine virtuelle Funktion zu
clone_impl()
erstellen, die aBase*
in der Basisklasse zurückgibt . In der abgeleiteten Klasse wird es jedoch über die Kovarianz erweitert, um a zurückzugebenDerived*
, und dieser Zeiger zeigt auf eine neu erstellte Kopie der abgeleiteten Klasse. Die Basisklasse kann dann über den Basisklassenzeiger auf dieses neue Objekt zugreifenBase*
, es in ein Objekt einschließenunique_ptr
und es über die eigentlicheclone()
Funktion zurückgeben, die von außen aufgerufen wird.quelle
unique_ptr
wenn die direkte Eindämmung etwas anderes tun würde. Die Antwort??? Vererbung .unique_ptr
überoptional
für Nullable Types.clone_impl
In-Base implementieren , sagt Ihnen der Compiler nicht, ob Sie sie in der abgeleiteten Klasse vergessen haben. Sie können jedoch eine andere Basisklasse verwendenCloneable
und dort eine reine virtuelle Klasse implementierenclone_impl
. Dann beschwert sich der Compiler, wenn Sie es in der abgeleiteten Klasse vergessen.Versuchen Sie diesen Helfer, um tiefe Kopien zu erstellen und zu bewältigen, wenn die Quelle unique_ptr null ist.
Z.B:
quelle
Daniel Frey erwähnt über Kopierlösung, ich würde darüber sprechen, wie man den unique_ptr verschiebt
Sie werden als Verschiebungskonstruktor und Verschiebungszuweisung bezeichnet
Sie könnten sie so verwenden
Sie müssen a und c mit std :: move umbrechen, da sie einen Namen haben. Std :: move weist den Compiler an, den Wert in eine r-Wert-Referenz umzuwandeln, unabhängig von den Parametern. Im technischen Sinne ist std :: move eine Analogie zu " std :: rvalue "
Nach dem Verschieben wird die Ressource des unique_ptr auf einen anderen unique_ptr übertragen
Es gibt viele Themen, die die Wertreferenz dokumentieren. Dies ist zunächst ziemlich einfach .
Bearbeiten:
Das bewegte Objekt bleibt gültig, aber nicht spezifiziert .
C ++ Primer 5, ch13 geben auch eine sehr gute Erklärung, wie das Objekt "verschoben" wird
quelle
a
nachdem std :: move (a) imb
move-Konstruktor aufgerufen wurde? Ist es einfach total ungültig?Ich schlage vor, make_unique zu verwenden
quelle
unique_ptr
ist nicht kopierbar, es ist nur beweglich.Dies wirkt sich direkt auf Test aus, der in Ihrem zweiten Beispiel ebenfalls nur beweglich und nicht kopierbar ist.
In der Tat ist es gut, dass Sie verwenden,
unique_ptr
was Sie vor einem großen Fehler schützt.Das Hauptproblem bei Ihrem ersten Code ist beispielsweise, dass der Zeiger niemals gelöscht wird, was wirklich sehr, sehr schlecht ist. Angenommen, Sie würden dies beheben durch:
Das ist auch schlecht. Was passiert, wenn Sie kopieren
Test
? Es gibt zwei Klassen mit einem Zeiger, der auf dieselbe Adresse zeigt.Wenn einer
Test
zerstört wird, zerstört er auch den Zeiger. Wenn Ihre SekundeTest
zerstört wird, wird versucht, auch den Speicher hinter dem Zeiger zu entfernen. Es wurde jedoch bereits gelöscht und es wird ein fehlerhafter Laufzeitfehler beim Speicherzugriff angezeigt (oder ein undefiniertes Verhalten, wenn wir Pech haben).Der richtige Weg ist also, entweder den Kopierkonstruktor und den Kopierzuweisungsoperator zu implementieren, damit das Verhalten klar ist und wir eine Kopie erstellen können.
unique_ptr
ist uns hier weit voraus. Es hat die semantische Bedeutung: " Ich bin esunique
, also können Sie mich nicht einfach kopieren. " Es verhindert also den Fehler, die vorliegenden Operatoren jetzt zu implementieren.Sie können den Kopierkonstruktor und den Kopierzuweisungsoperator für ein spezielles Verhalten definieren, und Ihr Code funktioniert. Aber Sie sind zu Recht (!) Dazu gezwungen.
Die Moral der Geschichte: Verwenden Sie immer
unique_ptr
in solchen Situationen.quelle