Gibt es eine Verwendung für unique_ptr mit Array?

238

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::vectoroder std::array.

Finden Sie eine Verwendung für dieses Konstrukt?

fen
quelle
6
Der Vollständigkeit halber sollte ich darauf hinweisen, dass es keine gibt 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 immer boost::shared_array.
Pseudonym
13
std::shared_ptr<T []> ist jetzt in c ++ 17.
力 力
Sie können mehrere Möglichkeiten finden, um etwas auf einem Computer zu tun. Dieses Konstrukt kann insbesondere in einem Hot-Path verwendet werden, da es den Overhead von Containeroperationen verringert, wenn Sie genau wissen, wie Sie auf Ihr Array abzielen. Darüber hinaus werden Zeichenarrays erstellt, ohne dass ein zusammenhängender Speicher erforderlich ist.
Kevr

Antworten:

256

Einige Leute haben nicht den Luxus std::vector, selbst mit Allokatoren zu verwenden. Einige Leute brauchen ein Array mit dynamischer Größe, also std::arrayist 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 ein vectoroder 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.

Nicol Bolas
quelle
27
@NoSenseEtAl: Ich bin mir nicht sicher, welcher Teil von "Manche Leute dürfen das nicht" dir entgeht. Einige Projekte haben sehr spezifische Anforderungen, darunter "Sie können sie nicht verwenden vector". Sie können argumentieren, ob dies angemessene Anforderungen sind oder nicht, aber Sie können nicht leugnen, dass sie existieren .
Nicol Bolas
21
Es gibt keinen Grund auf der Welt, warum jemand nicht in der Lage wäre, zu verwenden, std::vectorwenn er es verwenden könnte std::unique_ptr.
Miles Rout
66
Hier ist ein Grund, den Vektor nicht zu verwenden: sizeof (std :: vector <char>) == 24; sizeof (std :: unique_ptr <char []>) == 8
Arvid
13
@DanNissenbaum Diese Projekte existieren. In einigen Branchen, die einer sehr genauen Prüfung unterzogen werden, wie zum Beispiel der Luftfahrt oder der Verteidigung, ist die Standardbibliothek verboten, da es schwierig ist, zu überprüfen und zu beweisen, dass sie für die zuständigen Stellen, die die Vorschriften festlegen, korrekt ist. Sie können argumentieren, dass die Standardbibliothek gut getestet ist und ich würde Ihnen zustimmen, aber Sie und ich machen die Regeln nicht.
Emily L.
16
@DanNissenbaum Auch einige harte Echtzeitsysteme dürfen die dynamische Speicherzuweisung überhaupt nicht verwenden, da die Verzögerung, die ein Systemaufruf verursacht, möglicherweise nicht theoretisch begrenzt ist und Sie das Echtzeitverhalten des Programms nicht nachweisen können. Oder die Grenze ist möglicherweise zu groß, wodurch Ihr WCET-Limit überschritten wird. Obwohl hier nicht anwendbar, da sie auch nicht verwenden würden, unique_ptraber solche Projekte existieren tatsächlich.
Emily L.
124

Es gibt Kompromisse, und Sie wählen die Lösung aus, die Ihren Wünschen entspricht. Aus dem Kopf:

Ursprüngliche Größe

  • vectorund unique_ptr<T[]>lassen Sie die Größe zur Laufzeit angeben
  • array Ermöglicht nur die Angabe der Größe zur Kompilierungszeit

Größenänderung

  • arrayund unique_ptr<T[]>erlauben Sie keine Größenänderung
  • vector tut

Lager

  • vectorund unique_ptr<T[]>speichern Sie die Daten außerhalb des Objekts (normalerweise auf dem Heap).
  • array speichert die Daten direkt im Objekt

Kopieren

  • arrayund vectorerlauben das Kopieren
  • unique_ptr<T[]> erlaubt kein Kopieren

Tauschen / bewegen

  • vectorund unique_ptr<T[]>O (1) Zeit haben swapund Operationen verschieben
  • arrayhat O (n) Zeit- swapund Verschiebungsoperationen, wobei n die Anzahl der Elemente im Array ist

Ungü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 nicht swap()
  • 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

  • arrayund vectorsind beide Container
  • unique_ptr<T[]> ist kein Container

Ich muss zugeben, dies scheint eine Gelegenheit für ein Refactoring mit richtlinienbasiertem Design zu sein.

Pseudonym
quelle
1
Ich bin nicht sicher, ob ich verstehe, was Sie im Zusammenhang mit der Ungültigmachung von Zeigern meinen . Geht es um Zeiger auf die Objekte selbst oder um Zeiger auf die Elemente? Oder etwas anderes? Welche Garantie erhalten Sie von einem Array, das Sie nicht von einem Vektor erhalten?
Jogojapan
3
Angenommen, Sie haben einen Iterator, einen Zeiger oder einen Verweis auf ein Element von a vector. Dann erhöhen Sie die Größe oder Kapazität, vectorsodass eine Neuzuweisung erzwungen wird. Dann zeigt dieser Iterator, Zeiger oder Verweis nicht mehr auf dieses Element des vector. Das ist es, was wir unter "Ungültigmachung" verstehen. Dieses Problem tritt nicht auf array, da es keine "Neuzuweisung" gibt. Eigentlich habe ich gerade ein Detail damit bemerkt und es entsprechend bearbeitet.
Pseudonym
1
Ok, es kann keine Ungültigmachung aufgrund einer Neuzuweisung in einem Array oder 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.
Jogojapan
Ja, alle Wetten sind ungültig, wenn das Objekt nicht mehr aktiv ist.
Pseudonym
1
@rubenvb Sicher können Sie, aber Sie können Range-based for-Schleifen nicht direkt verwenden. Im Gegensatz zu einem Normalfall T[]muss die Größe (oder gleichwertige Informationen) übrigens irgendwo operator delete[]herumhängen, um die Elemente des Arrays korrekt zu zerstören. Es wäre schön, wenn der Programmierer Zugriff darauf hätte.
Pseudonym
73

Ein Grund, warum Sie a verwenden könnten, unique_ptrist, wenn Sie die Laufzeitkosten für die Wertinitialisierung des Arrays nicht bezahlen möchten .

std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars

std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars

Der std::vectorKonstruktor und std::vector::resize()wird den Wert initialisieren T- aber newnicht, wenn Tes sich um einen POD handelt.

Siehe Wertinitialisierte Objekte in C ++ 11 und std :: vector-Konstruktor

Beachten Sie, dass dies vector::reservehier 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über calloc.

Charles Salvia
quelle
Dieser Grund ist jedoch nicht die einzige Lösung .
Ruslan
@Ruslan In der verknüpften Lösung sind die Elemente des dynamischen Arrays noch wertinitialisiert, aber die Wertinitialisierung bewirkt nichts. Ich würde zustimmen, dass ein Optimierer, der nicht erkennt, dass 1000000 Mal nichts getan werden kann, ohne Code implementiert werden kann, keinen Cent wert ist, aber man könnte es vorziehen, überhaupt nicht von dieser Optimierung abhängig zu sein.
Marc van Leeuwen
Eine weitere Möglichkeit besteht darin, std::vectoreinen benutzerdefinierten Allokator bereitzustellen, der die Konstruktion von Typen std::is_trivially_default_constructibleund die Zerstörung von Objekten vermeidet std::is_trivially_destructible, obwohl dies strikt gegen den C ++ - Standard verstößt (da solche Typen nicht standardmäßig initialisiert werden).
Walter
Bietet std::unique_ptrauch keine gebundene Prüfung im Gegensatz zu vielen std::vectorImplementierungen.
Diapir
30

An std::vectorkann kopiert werden, während unique_ptr<int[]>der eindeutige Besitz des Arrays ausgedrückt werden kann. std::arrayAndererseits muss die Größe zur Kompilierungszeit bestimmt werden, was in einigen Situationen unmöglich sein kann.

Andy Prowl
quelle
2
Nur weil etwas kopiert werden kann , muss es nicht sein.
Nicol Bolas
4
@NicolBolas: Ich verstehe nicht. Man will, dass aus dem gleichen Grund verhindern , warum man benutzen würde unique_ptrstatt shared_ptr. Vermisse ich etwas
Andy Prowl
4
unique_ptrverhindert nicht nur versehentlichen Missbrauch. Es ist auch kleiner und geringer als shared_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. Und vectorist als Array-Speicher weitaus nützlicher als unique_ptr<T[]>, wenn auch aus keinem anderen Grund als der Tatsache, dass er eine Größe hat .
Nicol Bolas
3
Ich dachte, ich hätte den Punkt klargestellt: Es gibt andere Gründe , einen bestimmten Typ zu verwenden. Genau wie es Gründe gibt , zu bevorzugen vectorüber unique_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.
Nicol Bolas
8
std::vectorhat mehr Overhead als a std::unique_ptr- es werden ~ 3 Zeiger anstelle von ~ 1 verwendet. std::unique_ptrBlockiert die Kopierkonstruktion, aktiviert jedoch die Verschiebungskonstruktion. Wenn die Daten, mit denen Sie arbeiten, semantisch nur verschoben, aber nicht kopiert werden können, infiziert dies classdie 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 Ihnen std::vectorin eine Klasse einordnen zu müssen, in der Sie sie manuell deaktivieren move. std::unique_ptr<std::array>hat eine size.
Yakk - Adam Nevraumont
22

Scott Meyers hat dies in Effective Modern C ++ zu sagen

Die Existenz std::unique_ptrfür Arrays von nur intellektuellen Interesse sein sollten, weil std::array, std::vector, std::stringsind praktisch immer bessere Datenstruktur Auswahl als rohe Arrays. Die einzige Situation, die ich mir vorstellen kann, wenn std::unique_ptr<T[]>dies sinnvoll wäre, ist die Verwendung einer C-ähnlichen API, die einen Rohzeiger auf ein Heap-Array zurückgibt, dessen Eigentümer Sie sind.

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 sagen std::unique_ptr<T[]>?

Newling
quelle
4
Es hört sich so an, als hätte er sich einfach nicht einige Anwendungsfälle vorgestellt, nämlich einen Puffer, dessen Größe fest ist, der zur Kompilierungszeit jedoch unbekannt ist, und / oder einen Puffer, für den wir keine Kopien zulassen. Es gibt auch Effizienz als möglichen Grund, es gegenüber vector stackoverflow.com/a/24852984/2436175 zu bevorzugen .
Antonio
17

Im Gegensatz zu std::vectorund std::array, std::unique_ptrkann einen NULL - Zeiger besitzen.
Dies ist praktisch, wenn Sie mit C-APIs arbeiten, die entweder ein Array oder NULL erwarten:

void legacy_func(const int *array_or_null);

void some_func() {    
    std::unique_ptr<int[]> ptr;
    if (some_condition) {
        ptr.reset(new int[10]);
    }

    legacy_func(ptr.get());
}
George
quelle
10

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).

Simon Ferquel
quelle
9

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):

// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;

// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;

LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
    // Allocate buffer of specified length
    buffer.reset( BYTE[bufferLength] );
    //        
    // Or, in C++14, could use make_unique() instead, e.g.
    //
    // buffer = std::make_unique<BYTE[]>(bufferLength);
    //

    //
    // Call some Win32 API.
    //
    // If the size of the buffer (stored in 'bufferLength') is not big enough,
    // the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
    // in the [in, out] parameter 'bufferLength'.
    // In that case, there will be another try in the next loop iteration
    // (with the allocation of a bigger buffer).
    //
    // Else, we'll exit the while loop body, and there will be either a failure
    // different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
    // and the required information will be available in the buffer.
    //
    returnCode = ::SomeApiCall(inParam1, inParam2, inParam3, 
                               &bufferLength, // size of output buffer
                               buffer.get(),  // output buffer pointer
                               &outParam1, &outParam2);
}

if (Failed(returnCode))
{
    // Handle failure, or throw exception, etc.
    ...
}

// All right!
// Do some processing with the returned information...
...
Mr.C64
quelle
Sie könnten nur std::vector<char>in diesen Fällen verwenden.
Arthur Tacca
@ArthurTacca - ... wenn es Ihnen nichts ausmacht, dass der Compiler jedes Zeichen in Ihrem Puffer einzeln auf 0 initialisiert.
TED
9

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. Mit std::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::vectores nicht funktioniert hat und ich ein dynamisches Array sauber zuordnen musste? :-)

Der Quantenphysiker
quelle
9

Kurz gesagt: Es ist bei weitem das speichereffizienteste.

A std::stringwird 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 verwenden char *, und es wäre die meiste Zeit null. Was auch für C ++ funktioniert, außer dass a char *keinen Destruktor hat und nicht weiß, wie er sich selbst löschen soll. Im Gegensatz std::unique_ptr<char[]>dazu löscht sich a selbst, wenn es den Gültigkeitsbereich verlässt. Ein Leerzeichen std::stringbenötigt 32 Bytes, ein Leerzeichen std::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 strlenmuss.

jorgbrown
quelle
3

Um Leuten zu antworten, die denken, Sie müssten "verwenden", vectoranstatt unique_ptreinen 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 (mit cudaMalloc) entscheiden. Wenn Sie diese Daten dann in Host abrufen, müssen Sie erneut nach einem Zeiger unique_ptrsuchen. Der Zeiger kann problemlos verarbeitet werden. Die zusätzlichen Kosten für die Umstellung double*auf vector<double>sind unnötig und führen zu einem Leistungsverlust.

Romain Laneuville
quelle
3

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 #includeAnweisungen in Headern minimieren möchten (um die Build-Leistung zu optimieren).

Zum Beispiel -

myclass.h:

class ALargeAndComplicatedClassWithLotsOfDependencies;

class MyClass {
   ...
private:
   std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};

myclass.cpp:

#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"

// MyClass implementation goes here

Mit der obigen Codestruktur kann #include "myclass.h"und kann jeder MyClassBenutzer die internen Implementierungsabhängigkeiten berücksichtigen, die von erforderlich sind MyClass::m_InternalArray.

Wenn m_InternalArraystattdessen als a std::array<ALargeAndComplicatedClassWithLotsOfDependencies>bzw. a deklariert wurde std::vector<...>, wird versucht, einen unvollständigen Typ zu verwenden, was ein Fehler bei der Kompilierung ist.

Boris Shpungin
quelle
Für diesen speziellen Anwendungsfall würde ich mich für das Pimpl-Muster entscheiden, um die Abhängigkeit aufzuheben. Wenn es nur privat verwendet wird, kann die Definition verschoben werden, bis die Klassenmethoden implementiert sind. Wenn es öffentlich verwendet wird, sollten die Benutzer der Klasse bereits über das konkrete Wissen verfügen class ALargeAndComplicatedClassWithLotsOfDependencies. Logischerweise sollten Sie auf solche Szenarien nicht stoßen.
3

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:

       Dynamic     |   Runtime static   |         Static
Stack std::vector      unique_ptr<T[]>          std::array
Heap  std::vector      unique_ptr<T[]>     unique_ptr<std::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::arrayund std::vectoreine 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.

Violette Giraffe
quelle
2

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:

unique_ptr<byte[]> data = get_some_data();

threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
                      data.release());

Wir alle möchten, dass die Dinge für uns schön sind. C ++ ist für die anderen Zeiten.

Simon Buchan
quelle
2

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 Objekt stringoder vector<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).

Serge Rogatch
quelle
Man hat sie nicht alle in einem großen zusammengefasst 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 werden unique_ptr<char[]>. Dieses Schlüsselnugget fehlt jedoch in Ihrer Antwort.
Arthur Tacca
2
  • Aus Gründen der Binärkompatibilität muss Ihre Struktur nur einen Zeiger enthalten.
  • Sie müssen eine Schnittstelle mit einer API herstellen, die den zugewiesenen Speicher zurückgibt new[]
  • Ihre Firma oder Ihr Projekt hat eine allgemeine Regel gegen die Verwendung std::vector, um beispielsweise zu verhindern, dass unachtsame Programmierer versehentlich Kopien einführen
  • Sie möchten verhindern, dass unachtsame Programmierer in diesem Fall versehentlich Kopien einführen.

Es 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.

Jimmy Hartzell
quelle
0

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?

Ilia Minkin
quelle