std::unique_ptr
unterstützt Arrays, zum Beispiel:
std::unique_ptr<int[]> p(new int[10]);
aber wird es gebraucht? wahrscheinlich ist es bequemer zu bedienen std::vector
oder std::array
.
Finden Sie eine Verwendung für dieses Konstrukt?
std::unique_ptr
unterstützt Arrays, zum Beispiel:
std::unique_ptr<int[]> p(new int[10]);
aber wird es gebraucht? wahrscheinlich ist es bequemer zu bedienen std::vector
oder std::array
.
Finden Sie eine Verwendung für dieses Konstrukt?
std::shared_ptr<T[]>
, aber es sollte und wahrscheinlich in C ++ 14 geben wird, wenn sich jemand die Mühe machen könnte, einen Vorschlag zu verfassen. In der Zwischenzeit gibt es immerboost::shared_array
.std::shared_ptr
<T []> ist jetzt in c ++ 17.Antworten:
Einige Leute haben nicht den Luxus
std::vector
, selbst mit Allokatoren zu verwenden. Einige Leute brauchen ein Array mit dynamischer Größe, alsostd::array
ist es raus. Und einige Leute beziehen ihre Arrays aus anderem Code, von dem bekannt ist, dass er ein Array zurückgibt. und dieser Code wird nicht umgeschrieben, um einvector
oder etwas zurückzugeben.Indem Sie zulassen
unique_ptr<T[]>
, bedienen Sie diese Anforderungen.Kurz gesagt, Sie verwenden,
unique_ptr<T[]>
wenn Sie müssen . Wenn die Alternativen für Sie einfach nicht funktionieren. Es ist ein Werkzeug der letzten Instanz.quelle
vector
". Sie können argumentieren, ob dies angemessene Anforderungen sind oder nicht, aber Sie können nicht leugnen, dass sie existieren .std::vector
wenn er es verwenden könntestd::unique_ptr
.unique_ptr
aber solche Projekte existieren tatsächlich.Es gibt Kompromisse, und Sie wählen die Lösung aus, die Ihren Wünschen entspricht. Aus dem Kopf:
Ursprüngliche Größe
vector
undunique_ptr<T[]>
lassen Sie die Größe zur Laufzeit angebenarray
Ermöglicht nur die Angabe der Größe zur KompilierungszeitGrößenänderung
array
undunique_ptr<T[]>
erlauben Sie keine Größenänderungvector
tutLager
vector
undunique_ptr<T[]>
speichern Sie die Daten außerhalb des Objekts (normalerweise auf dem Heap).array
speichert die Daten direkt im ObjektKopieren
array
undvector
erlauben das Kopierenunique_ptr<T[]>
erlaubt kein KopierenTauschen / bewegen
vector
undunique_ptr<T[]>
O (1) Zeit habenswap
und Operationen verschiebenarray
hat O (n) Zeit-swap
und Verschiebungsoperationen, wobei n die Anzahl der Elemente im Array istUngültigmachung des Zeigers / der Referenz / des Iterators
array
stellt sicher, dass Zeiger, Referenzen und Iteratoren niemals ungültig werden, solange das Objekt aktiv ist, auch nichtswap()
unique_ptr<T[]>
hat keine Iteratoren; Zeiger und Referenzen werden nur ungültig,swap()
solange das Objekt aktiv ist. (Nach dem Tauschen zeigen Zeiger auf das Array, mit dem Sie getauscht haben, sodass sie in diesem Sinne immer noch "gültig" sind.)vector
kann Zeiger, Referenzen und Iteratoren bei jeder Neuzuweisung ungültig machen (und bietet einige Garantien dafür, dass die Neuzuweisung nur bei bestimmten Vorgängen erfolgen kann).Kompatibilität mit Konzepten und Algorithmen
array
undvector
sind beide Containerunique_ptr<T[]>
ist kein ContainerIch muss zugeben, dies scheint eine Gelegenheit für ein Refactoring mit richtlinienbasiertem Design zu sein.
quelle
vector
. Dann erhöhen Sie die Größe oder Kapazität,vector
sodass eine Neuzuweisung erzwungen wird. Dann zeigt dieser Iterator, Zeiger oder Verweis nicht mehr auf dieses Element desvector
. Das ist es, was wir unter "Ungültigmachung" verstehen. Dieses Problem tritt nicht aufarray
, da es keine "Neuzuweisung" gibt. Eigentlich habe ich gerade ein Detail damit bemerkt und es entsprechend bearbeitet.unique_ptr<T[]>
weil es keine Neuzuweisung gibt. Wenn das Array den Gültigkeitsbereich verlässt, werden Zeiger auf bestimmte Elemente natürlich weiterhin ungültig.T[]
muss die Größe (oder gleichwertige Informationen) übrigens irgendwooperator delete[]
herumhängen, um die Elemente des Arrays korrekt zu zerstören. Es wäre schön, wenn der Programmierer Zugriff darauf hätte.Ein Grund, warum Sie a verwenden könnten,
unique_ptr
ist, wenn Sie die Laufzeitkosten für die Wertinitialisierung des Arrays nicht bezahlen möchten .Der
std::vector
Konstruktor undstd::vector::resize()
wird den Wert initialisierenT
- abernew
nicht, wennT
es sich um einen POD handelt.Siehe Wertinitialisierte Objekte in C ++ 11 und std :: vector-Konstruktor
Beachten Sie, dass dies
vector::reserve
hier keine Alternative ist: Ist der Zugriff auf den Rohzeiger nach std :: vector :: Reserve sicher?Es ist der gleiche Grund , ein C - Programmierer wählen könnte
malloc
übercalloc
.quelle
std::vector
einen benutzerdefinierten Allokator bereitzustellen, der die Konstruktion von Typenstd::is_trivially_default_constructible
und die Zerstörung von Objekten vermeidetstd::is_trivially_destructible
, obwohl dies strikt gegen den C ++ - Standard verstößt (da solche Typen nicht standardmäßig initialisiert werden).std::unique_ptr
auch keine gebundene Prüfung im Gegensatz zu vielenstd::vector
Implementierungen.An
std::vector
kann kopiert werden, währendunique_ptr<int[]>
der eindeutige Besitz des Arrays ausgedrückt werden kann.std::array
Andererseits muss die Größe zur Kompilierungszeit bestimmt werden, was in einigen Situationen unmöglich sein kann.quelle
unique_ptr
stattshared_ptr
. Vermisse ich etwasunique_ptr
verhindert nicht nur versehentlichen Missbrauch. Es ist auch kleiner und geringer alsshared_ptr
. Der Punkt ist, dass es zwar schön ist, Semantik in einer Klasse zu haben, die "Missbrauch" verhindert, aber nicht der einzige Grund ist, einen bestimmten Typ zu verwenden. Undvector
ist als Array-Speicher weitaus nützlicher alsunique_ptr<T[]>
, wenn auch aus keinem anderen Grund als der Tatsache, dass er eine Größe hat .vector
überunique_ptr<T[]>
möglichst anstatt nur sagen : „Sie es nicht kopieren kann“ und deshalb wählen ,unique_ptr<T[]>
wenn Sie keine Kopien wollen tun. Jemanden davon abzuhalten, das Falsche zu tun, ist nicht unbedingt der wichtigste Grund, eine Klasse auszuwählen.std::vector
hat mehr Overhead als astd::unique_ptr
- es werden ~ 3 Zeiger anstelle von ~ 1 verwendet.std::unique_ptr
Blockiert die Kopierkonstruktion, aktiviert jedoch die Verschiebungskonstruktion. Wenn die Daten, mit denen Sie arbeiten, semantisch nur verschoben, aber nicht kopiert werden können, infiziert diesclass
die Daten. Wenn Sie eine Operation für ungültige Daten ausführen, wird Ihre Containerklasse tatsächlich schlechter, und "Verwenden Sie sie einfach nicht" werden nicht alle Sünden weggespült. Es ist ein Problem, jede Instanz von Ihnenstd::vector
in eine Klasse einordnen zu müssen, in der Sie sie manuell deaktivierenmove
.std::unique_ptr<std::array>
hat einesize
.Scott Meyers hat dies in Effective Modern C ++ zu sagen
Ich denke jedoch, dass die Antwort von Charles Salvia relevant ist:
std::unique_ptr<T[]>
Nur so kann ein leeres Array initialisiert werden, dessen Größe zum Zeitpunkt der Kompilierung nicht bekannt ist. Was würde Scott Meyers zu dieser Motivation sagenstd::unique_ptr<T[]>
?quelle
vector
stackoverflow.com/a/24852984/2436175 zu bevorzugen .Im Gegensatz zu
std::vector
undstd::array
,std::unique_ptr
kann einen NULL - Zeiger besitzen.Dies ist praktisch, wenn Sie mit C-APIs arbeiten, die entweder ein Array oder NULL erwarten:
quelle
Ich habe
unique_ptr<char[]>
vorab zugewiesene Speicherpools implementiert, die in einer Spiel-Engine verwendet werden. Die Idee ist, vorab zugewiesene Speicherpools bereitzustellen, die anstelle dynamischer Zuweisungen für die Rückgabe von Kollisionsanforderungsergebnissen und anderen Dingen wie der Teilchenphysik verwendet werden, ohne dass bei jedem Frame Speicher zugewiesen / freigegeben werden muss. Dies ist für solche Szenarien sehr praktisch, in denen Sie Speicherpools benötigen, um Objekte mit begrenzter Lebensdauer (normalerweise ein, zwei oder drei Frames) zuzuweisen, für die keine Zerstörungslogik erforderlich ist (nur Speicherfreigabe).quelle
Ein allgemeines Muster findet sich in einigen Windows Win32-API- Aufrufen, in denen die Verwendung von
std::unique_ptr<T[]>
nützlich sein kann, z. B. wenn Sie nicht genau wissen, wie groß ein Ausgabepuffer sein soll, wenn Sie eine Win32-API aufrufen (die einige Daten in das Programm schreibt) dieser Puffer):quelle
std::vector<char>
in diesen Fällen verwenden.Ich sah mich einem Fall gegenüber, in dem ich verwenden musste
std::unique_ptr<bool[]>
, der sich in der HDF5-Bibliothek befand (eine Bibliothek zur effizienten Speicherung von Binärdaten, die in der Wissenschaft häufig verwendet wird). Einige Compiler (in meinem Fall Visual Studio 2015) bieten eine Komprimierung vonstd::vector<bool>
(durch Verwendung von 8 Bools in jedem Byte), was eine Katastrophe für etwas wie HDF5 ist, das sich nicht um diese Komprimierung kümmert. Mitstd::vector<bool>
, HDF5 las schließlich Müll wegen dieser Komprimierung.Ratet mal, wer für die Rettung da war, in einem Fall, in dem
std::vector
es nicht funktioniert hat und ich ein dynamisches Array sauber zuordnen musste? :-)quelle
Kurz gesagt: Es ist bei weitem das speichereffizienteste.
A
std::string
wird mit einem Zeiger, einer Länge und einem Puffer zur Optimierung kurzer Zeichenfolgen geliefert. Aber meine Situation ist, dass ich eine Zeichenfolge, die fast immer leer ist, in einer Struktur speichern muss, von der ich Hunderttausende habe. In C würde ich nur verwendenchar *
, und es wäre die meiste Zeit null. Was auch für C ++ funktioniert, außer dass achar *
keinen Destruktor hat und nicht weiß, wie er sich selbst löschen soll. Im Gegensatzstd::unique_ptr<char[]>
dazu löscht sich a selbst, wenn es den Gültigkeitsbereich verlässt. Ein Leerzeichenstd::string
benötigt 32 Bytes, ein Leerzeichenstd::unique_ptr<char[]>
8 Bytes, dh genau die Größe seines Zeigers.Der größte Nachteil ist, dass ich jedes Mal, wenn ich die Länge der Saite wissen möchte, darauf zurückgreifen
strlen
muss.quelle
Um Leuten zu antworten, die denken, Sie müssten "verwenden",
vector
anstattunique_ptr
einen Fall in der CUDA-Programmierung auf der GPU zu haben, wenn Sie Speicher im Gerät zuweisen, müssen Sie sich für ein Zeigerarray (mitcudaMalloc
) entscheiden. Wenn Sie diese Daten dann in Host abrufen, müssen Sie erneut nach einem Zeigerunique_ptr
suchen. Der Zeiger kann problemlos verarbeitet werden. Die zusätzlichen Kosten für die Umstellungdouble*
aufvector<double>
sind unnötig und führen zu einem Leistungsverlust.quelle
Ein weiterer Grund zum Zulassen und Verwenden
std::unique_ptr<T[]>
, der in den Antworten bisher nicht erwähnt wurde: Sie können den Array-Elementtyp vorwärts deklarieren.Dies ist nützlich, wenn Sie die verketteten
#include
Anweisungen in Headern minimieren möchten (um die Build-Leistung zu optimieren).Zum Beispiel -
Mit der obigen Codestruktur kann
#include "myclass.h"
und kann jederMyClass
Benutzer die internen Implementierungsabhängigkeiten berücksichtigen, die von erforderlich sindMyClass::m_InternalArray
.Wenn
m_InternalArray
stattdessen als astd::array<ALargeAndComplicatedClassWithLotsOfDependencies>
bzw. a deklariert wurdestd::vector<...>
, wird versucht, einen unvollständigen Typ zu verwenden, was ein Fehler bei der Kompilierung ist.quelle
class ALargeAndComplicatedClassWithLotsOfDependencies
. Logischerweise sollten Sie auf solche Szenarien nicht stoßen.Ich kann dem Geist der akzeptierten Antwort nicht stark genug widersprechen. "Ein Werkzeug der letzten Instanz"? Weit davon entfernt!
Aus meiner Sicht ist eines der stärksten Merkmale von C ++ im Vergleich zu C und einigen anderen ähnlichen Sprachen die Fähigkeit, Einschränkungen auszudrücken, damit sie beim Kompilieren überprüft und ein versehentlicher Missbrauch verhindert werden kann. Fragen Sie sich beim Entwerfen einer Struktur, welche Operationen dies zulassen soll. Alle anderen Verwendungen sollten verboten werden, und es ist am besten, wenn solche Einschränkungen statisch (zur Kompilierungszeit) implementiert werden können, damit ein Missbrauch zu einem Kompilierungsfehler führt.
Wenn man also ein Array benötigt, geben die Antworten auf die folgenden Fragen sein Verhalten an: 1. Ist seine Größe a) zur Laufzeit dynamisch oder b) statisch, aber nur zur Laufzeit bekannt, oder c) statisch und zur Kompilierungszeit bekannt? 2. Kann das Array auf dem Stapel zugewiesen werden oder nicht?
Und basierend auf den Antworten sehe ich dies als die beste Datenstruktur für ein solches Array:
Ja, ich denke,
unique_ptr<std::array>
sollte auch in Betracht gezogen werden, und keines ist ein Werkzeug der letzten Instanz. Denken Sie nur daran, was am besten zu Ihrem Algorithmus passt.Alle diese sind mit einfachen C-APIs über den Rohzeiger auf das Datenarray (
vector.data()
/array.data()
/uniquePtr.get()
) kompatibel .PS Abgesehen von den oben genannten Überlegungen gibt es auch eine der Eigentumsverhältnisse:
std::array
undstd::vector
eine Wertsemantik (native Unterstützung für das Kopieren und Übergeben von Werten),unique_ptr<T[]>
die nur verschoben werden kann (erzwingt Einzelbesitz). Beides kann in verschiedenen Szenarien nützlich sein. Im Gegenteil, einfache statische Arrays (int[N]
) und einfache dynamische Arrays (new int[10]
) bieten keine und sollten daher nach Möglichkeit vermieden werden - was in den allermeisten Fällen möglich sein sollte. Wenn dies nicht ausreicht, bieten einfache dynamische Arrays auch keine Möglichkeit, ihre Größe abzufragen - zusätzliche Möglichkeit für Speicherbeschädigungen und Sicherheitslücken.quelle
Sie sind möglicherweise die richtige Antwort, wenn Sie nur einen einzigen Zeiger durch eine vorhandene API (Think Window Message oder Threading-bezogene Callback-Parameter) stecken können, die ein gewisses Maß an Lebensdauer haben, nachdem sie auf der anderen Seite der Luke "abgefangen" wurden. aber was hat nichts mit dem aufrufenden Code zu tun:
Wir alle möchten, dass die Dinge für uns schön sind. C ++ ist für die anderen Zeiten.
quelle
unique_ptr<char[]>
kann verwendet werden, wenn Sie die Leistung von C und die Bequemlichkeit von C ++ wünschen. Stellen Sie sich vor, Sie müssen mit Millionen (ok, Milliarden, wenn Sie noch nicht vertrauen) Strings arbeiten. Das Speichern jedes einzelnen in einem separaten Objektstring
odervector<char>
Objekt wäre eine Katastrophe für die Speicherverwaltungsroutinen (Heap). Vor allem, wenn Sie verschiedene Zeichenfolgen mehrmals zuweisen und löschen müssen.Sie können jedoch einen einzelnen Puffer zum Speichern so vieler Zeichenfolgen zuweisen. Sie möchten
char* buffer = (char*)malloc(total_size);
aus offensichtlichen Gründen nicht (wenn nicht offensichtlich, suchen Sie nach "Warum Smart PTRs verwenden"). Sie möchten lieberunique_ptr<char[]> buffer(new char[total_size]);
Analog dazu gelten für Nichtdaten dieselben Überlegungen zu Leistung und Zweckmäßigkeit
char
(berücksichtigen Sie Millionen von Vektoren / Matrizen / Objekten).quelle
vector<char>
? Ich nehme an, die Antwort ist, dass sie beim Erstellen des Puffers auf Null initialisiert werden, während sie bei Verwendung nicht gepuffert werdenunique_ptr<char[]>
. Dieses Schlüsselnugget fehlt jedoch in Ihrer Antwort.new[]
std::vector
, um beispielsweise zu verhindern, dass unachtsame Programmierer versehentlich Kopien einführenEs gibt eine allgemeine Regel, dass C ++ - Container dem Rolling-Your-Own mit Zeigern vorzuziehen sind. Es ist eine allgemeine Regel; es hat Ausnahmen. Es gibt mehr; Dies sind nur Beispiele.
quelle
Wenn Sie ein dynamisches Array von Objekten benötigen, die nicht kopierkonstruierbar sind, ist ein intelligenter Zeiger auf ein Array der richtige Weg. Was ist zum Beispiel, wenn Sie ein Array von Atomics benötigen?
quelle