Ich habe viele Antworten gesehen, die vorschlagen, intelligente Zeiger zu verwenden, um dynamisch zugewiesene Arrays im Speicher zu halten. Meine Ansicht war immer, dass wenn die Größe bekannt ist, sollte sie in ein std::array
gehüllt werden, und ebenso, wenn die Größe nicht bekannt war (dh indem der Zeigertyp des intelligenten Zeigers ein Array unbekannter Grenzen und reset()
gemacht wird) Später sollte man std::vector
verwenden. Und das tue ich auch immer.
Zum Beispiel habe ich kürzlich eine Antwort gesehen, die std::unique_ptr<int[5]> p(new int[5])
verwendet. Dies scheint analog zur Erstellung eines std::array<int, 5>
zu sein, da die Größe bekannt ist. Die zusätzlichen Vorteile sind, dass std::array
sein Array statisch zuordnet und Array-ähnliche Features wie die Größe, Iteratoren und mehr enthält.
Was sind also die Gründe für die Verwendung eines Smartpointers, um ein Array über die Verwendung anderer Standardcontainer zu halten, die speziell für diesen Zweck erstellt wurden?
Erstens benötigt unique_ptr<T[]>
keine statische Größe - es kann nur unique_ptr<int[]>
sein.
Außerdem sind die zusätzlichen Vorteile, die std :: array statisch zuweist sein Array
Dies ist nicht unbedingt ein Vorteil . Überlegen Sie, ob ich ein 10-Megabyte-Array habe - das wird meinen Stack sprengen.
Im Allgemeinen wählen Leute diesen Ansatz, wenn sie möchten, dass die Array-Größe bei der Erstellung festgelegt wird, aber die Mitglieder mutieren können. Beachten Sie, dass Sie für std::vector
nur beide die Elemente und für vector
const
oder keines von beiden erstellen können. Sie können nicht nur den Vektor, sondern die Elemente const
erstellen.
Wie Sie gesagt haben, ist std::array
ein statisches Array. Wenn Sie std::array<int, 5>
auf dem Stapel deklarieren, befindet sich das tatsächliche Array auf dem Stapel. Während std::unique_ptr<int[]>(new int[5])
stattdessen das tatsächliche Array auf den Heap setzt. Das mag für kleine Arrays kein großer Unterschied sein, aber für große Arrays ist es eine große Sache. Nur weil ein Array eine feste Länge hat, heißt das nicht, dass es immer auf dem Stack sein sollte. Dies hängt von der Größe des Arrays und davon ab, wie viel Stack-Speicherplatz verfügbar ist. Wenn Sie ein großes Array auf den Stack legen, besteht die Gefahr, dass ein Stack-Überlauffehler auftritt. Wenn Sie ein großes Array auf den Heap setzen, wird dies nicht ausgelöst (es sei denn, der Stack ist bereits voll, so dass das unique_ptr
nicht erstellt werden kann, nachdem das Array zugewiesen wurde), obwohl das Risiko eines Speichermangels beim Heap geringer ist / memmgr ist voll, aber das würde passieren, bevor die Unique_ptr
erstellt wurde.
Sie können einen eindeutigen Zeiger auf ein Array verwenden, wenn Sie die Zeit zur Laufzeit kennen, aber es wird sich nicht ändern, dies garantiert, dass Sie nur für diese Objekte Speicherplatz zuweisen und nicht mehr. Mit einem Vektor könnte man intern mehr Speicherplatz zuweisen, es ist implementation-defined, aber um eine gute amortisierte Zeit für das Einfügen zu erhalten, weist es normalerweise mindestens das Doppelte dessen zu, was es jedes Mal benötigt, wenn der Vektor wächst, da es kopiert werden muss.
Der Kompromiss zwischen std::vector
und std::array
ist einfach: "Ist die Größe immer gleich?".
Dann ist die Entscheidung "ob ich einen Smart Pointer haben möchte oder nicht" für jeden anderen Typ gleich. Möchte ich es als gemeinsames Objekt weitergeben? Verwende std::shared_ptr
. Will ich ein großes Objekt weitergeben, ohne Kopien zu machen? Verwende std::unique_ptr
.
Nehmen wir an, Sie haben einen Puffer von 2048, den Sie in Ihrem Programm weitergeben möchten, aber nicht viele Kopien. Sie könnten std::shared_ptr<std::array<unsigned char, 2048>>
dafür verwenden.
Die intelligenten Zeiger für Arrays sind genau das - intelligente Zeiger. Daher sollten Sie sie dort verwenden, wo eine sinnvolle Zeigerverwendung sinnvoll ist.
Angenommen, Sie haben eine ältere Codebase mit einer Funktion, die ein Array erstellt, das Sie später delete[]
haben müssen. Wie werden Sie diesen Code ausnahmesicher machen? Einfach, speichern Sie es im Smart Pointer.
Ähnliches Szenario: Die Legacy-Funktion möchte Eigentümer des Arrays sein:
%Vor% Versuchen Sie das mit std::vector
!
Während die richtige Lösung darin besteht, Ihr Design zu ändern, kann es soziale und praktische Gründe geben, warum Sie es nicht tun (Code von Drittanbietern?). Oder vielleicht Code Schritt für Schritt Refactoring?
Es gibt keinen Grund, warum Container nicht über std::unique_ptr
implementiert werden könnten. Ich sehe keinen Grund, warum jemand, der handgemachte Container schreibt, nicht von vorhandenen "Richtlinien-Handlern" profitieren könnte, anstatt Code manuell zu schreiben, um Rule of Three / Five zu erfüllen.
Ja, Sie sollten Behälter bevorzugen. Ja, std::unique_ptr<T[]>
ist nicht das nützlich.