Czy jest jakiś użytek dla Unique_ptr z tablicą?

238

std::unique_ptr obsługuje tablice, na przykład:

std::unique_ptr<int[]> p(new int[10]);

ale czy jest to potrzebne? prawdopodobnie jest wygodniejszy w użyciu std::vectorlub std::array.

Czy znajdujesz zastosowanie dla tego konstruktu?

bagnisko
źródło
6
Dla kompletności powinienem zaznaczyć, że nie ma std::shared_ptr<T[]>, ale powinno być i prawdopodobnie będzie w C ++ 14, jeśli ktokolwiek będzie miał problem z napisaniem propozycji. W międzyczasie zawsze jest boost::shared_array.
pseudonim
13
std::shared_ptr<T []> jest teraz w c ++ 17.
Możesz znaleźć wiele sposobów na zrobienie czegokolwiek na komputerze. Ta konstrukcja ma zastosowanie, szczególnie na gorącej ścieżce, ponieważ eliminuje narzut operacji kontenerowych, jeśli wiesz dokładnie, jak celować w tablicę. Ponadto tworzy tablice znaków bez żadnych wątpliwości dotyczących ciągłego przechowywania.
kevr

Odpowiedzi:

256

Niektóre osoby nie mają luksusu korzystania std::vector, nawet z przydzielającymi. Niektóre osoby potrzebują dynamicznej tablicy, więc nie std::arrayma. Niektóre osoby pobierają tablice z innego kodu, o którym wiadomo, że zwraca tablicę; i ten kod nie zostanie przepisany w celu zwrócenia vectorczegoś lub czegoś.

Pozwalając unique_ptr<T[]>, zaspokajasz te potrzeby.

Krótko mówiąc, używasz, unique_ptr<T[]>kiedy potrzebujesz . Kiedy alternatywy po prostu nie będą dla ciebie działać. To narzędzie ostateczności.

Nicol Bolas
źródło
27
@NoSenseEtAl: Nie jestem pewien, która część „niektórym ludziom nie wolno tego robić” umyka tobie. Niektóre projekty mają bardzo specyficzne wymagania, a wśród nich może być „nie można używać vector”. Możesz spierać się, czy są to rozsądne wymagania, czy nie, ale nie możesz zaprzeczyć, że one istnieją .
Nicol Bolas
21
Na świecie nie ma powodu, dla którego ktoś nie byłby w stanie korzystać, std::vectorgdyby mógł std::unique_ptr.
Miles Rout
66
oto powód, dla którego nie należy używać wektora: sizeof (std :: vector <char>) == 24; sizeof (std :: unique_ptr <char []>) == 8
Arvid
13
@DanNissenbaum Te projekty istnieją. W niektórych branżach, które podlegają bardzo ścisłej kontroli, takich jak na przykład lotnictwo czy obrona, standardowa biblioteka jest niedostępna, ponieważ trudno jest zweryfikować i udowodnić, że jest zgodna z przepisami jakiegokolwiek organu zarządzającego. Możesz argumentować, że standardowa biblioteka jest dobrze przetestowana i zgodziłbym się z tobą, ale ty i ja nie ustalamy reguł.
Emily L.
16
@ DanNissenbaum Również niektóre twarde systemy czasu rzeczywistego nie mogą w ogóle używać dynamicznego przydziału pamięci, ponieważ opóźnienie wywołane wywołaniem systemowym może nie być teoretycznie ograniczone i nie można udowodnić zachowania programu w czasie rzeczywistym. Lub granica może być zbyt duża, co przekracza limit WCET. Chociaż nie ma tutaj zastosowania, ponieważ nie skorzystaliby z unique_ptrnich, ale takie projekty naprawdę istnieją.
Emily L.,
124

Są kompromisy i wybierasz rozwiązanie, które pasuje do tego, co chcesz. Z czubka mojej głowy:

Początkowy rozmiar

  • vectori unique_ptr<T[]>zezwól na określenie rozmiaru w czasie wykonywania
  • array pozwala tylko określić rozmiar w czasie kompilacji

Zmiana rozmiaru

  • arrayi unique_ptr<T[]>nie zezwalaj na zmianę rozmiaru
  • vector robi

Przechowywanie

  • vectori unique_ptr<T[]>przechowuj dane poza obiektem (zwykle na stercie)
  • array przechowuje dane bezpośrednio w obiekcie

Biurowy

  • arrayi vectorzezwól na kopiowanie
  • unique_ptr<T[]> nie zezwala na kopiowanie

Zamień / przenieś

  • vectori unique_ptr<T[]>mieć O (1) swapoperacje czasu i ruchu
  • arrayma O (n) swapoperacji ruchu i czasu , gdzie n jest liczbą elementów w tablicy

Unieważnienie wskaźnika / odwołania / iteratora

  • array zapewnia, że ​​wskaźniki, referencje i iteratory nigdy nie zostaną unieważnione, gdy obiekt jest aktywny, nawet na swap()
  • unique_ptr<T[]>nie ma iteratorów; wskaźniki i referencje są unieważniane tylko wtedy, swap()gdy obiekt jest aktywny. (Po zamianie wskaźniki wskazują na tablicę, którą zamieniłeś, więc nadal są „prawidłowe” w tym sensie).
  • vector może unieważnić wskaźniki, referencje i iteratory przy każdej realokacji (i zapewnia pewne gwarancje, że realokacja może nastąpić tylko w przypadku niektórych operacji).

Zgodność z pojęciami i algorytmami

  • arrayi vectoroba są kontenerami
  • unique_ptr<T[]> nie jest kontenerem

Muszę przyznać, że wygląda to na okazję do pewnego refaktoryzacji dzięki projektowi opartemu na zasadach.

Pseudonim
źródło
1
Nie jestem pewien, czy rozumiem, co masz na myśli w kontekście unieważnienia wskaźnika . Czy chodzi o wskaźniki do samych obiektów, czy o elementy? Albo coś innego? Jaką gwarancję otrzymujesz z tablicy, której nie otrzymujesz z wektora?
jogojapan
3
Załóżmy, że masz iterator, wskaźnik lub odwołanie do element vector. Następnie zwiększasz rozmiar lub pojemność, vectortak że wymusza to realokację. Wówczas iterator, wskaźnik lub odwołanie nie wskazują już tego elementu vector. To właśnie rozumiemy przez „unieważnienie”. Ten problem się nie zdarza array, ponieważ nie ma „realokacji”. Właściwie właśnie zauważyłem jakiś szczegół i zredagowałem go, aby pasował.
pseudonim
1
Ok, nie może nastąpić unieważnienie w wyniku realokacji w tablicy lub unique_ptr<T[]>ponieważ nie ma realokacji. Ale oczywiście, gdy tablica wykracza poza zakres, wskaźniki do określonych elementów nadal będą unieważniane.
jogojapan
Tak, wszystkie zakłady są wyłączone, jeśli obiekt nie jest już aktywny.
pseudonim
1
@rubenvb Jasne, że możesz, ale nie możesz (powiedzmy) używać bezpośrednio pętli opartych na zakresie. Nawiasem mówiąc, inaczej niż normalnie T[], rozmiar (lub równoważna informacja) musi gdzieś wisieć, operator delete[]aby poprawnie zniszczyć elementy tablicy. Byłoby miło, gdyby programista miał do tego dostęp.
pseudonim
73

Jednym z powodów, dla których możesz użyć, unique_ptrjest to, że nie chcesz ponosić kosztów w czasie wykonywania inicjalizacji wartości tablicy.

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

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

std::vectorKonstruktor i std::vector::resize()będą wartości inicjalizacji T- ale newnie zrobi tego, jeśli Tjest POD.

Zobacz Obiekty inicjowane przez wartość w C ++ 11 i konstruktor std :: vector

Zauważ, że vector::reservetutaj nie ma alternatywy: czy dostęp do surowego wskaźnika po std :: vector :: Reserve jest bezpieczny?

To z tego samego powodu programista C może zdecydować mallocsię calloc.

Charles Salvia
źródło
Ale ten powód nie jest jedynym rozwiązaniem .
Ruslan
@ Ruslan W połączonym rozwiązaniu elementy tablicy dynamicznej są nadal inicjowane wartościowo, ale inicjalizacja wartości nic nie robi. Zgodziłbym się z tym, że optymalizator, który nie zdaje sobie sprawy, że nic nie robiąc 1000000 razy nie może zostać zaimplementowany przez żaden kod, nie jest wart ani grosza, ale można by nie chcieć wcale polegać na tej optymalizacji.
Marc van Leeuwen,
Jeszcze inną możliwością jest zapewnienie, aby std::vectorw niestandardowym podzielnika który unika budowę typów, które są std::is_trivially_default_constructiblei niszczenie obiektów, które std::is_trivially_destructible, choć ściśle ten narusza standard C ++ (ponieważ takie typy nie są domyślnie zainicjowane).
Walter,
Również std::unique_ptrnie zapewnia sprawdzanie zakresu przeciwieństwie do wielu std::vectorimplementacjach.
diapir
@diapir Nie chodzi o implementację: std::vectorjest wymagany przez Standard do sprawdzenia granic .at(). Myślę, że miałeś na myśli, że niektóre implementacje mają tryby debugowania, które również się sprawdzają .operator[], ale uważam, że jest to bezużyteczne do pisania dobrego, przenośnego kodu.
underscore_d
30

std::vectorMogą być kopiowane dookoła, natomiast unique_ptr<int[]>umożliwia wyrażanie unikalną własność tablicy. std::arrayz drugiej strony wymaga określenia rozmiaru w czasie kompilacji, co może być niemożliwe w niektórych sytuacjach.

Andy Prowl
źródło
2
To, że coś można skopiować, nie oznacza, że ​​tak musi być.
Nicol Bolas
4
@NicolBolas: Nie rozumiem. Można temu zapobiec z tego samego powodu, dla którego użyłby unique_ptrzamiast shared_ptr. Czy coś brakuje?
Andy Prowl
4
unique_ptrnie tylko zapobiega przypadkowemu niewłaściwemu użyciu. Jest również mniejszy i niższy narzut niż shared_ptr. Chodzi o to, że chociaż fajnie jest mieć semantykę w klasie, która zapobiega „niewłaściwemu użyciu”, nie jest to jedyny powód, aby używać określonego typu. I vectorjest o wiele bardziej przydatny jako pamięć tablicowa niż unique_ptr<T[]>, jeśli bez żadnego innego powodu niż fakt, że ma rozmiar .
Nicol Bolas
3
Pomyślałem, że wyjaśniłem sprawę: istnieją inne powody, aby używać określonego typu niż ten. Podobnie jak istnieją powody, aby wolą vectorna unique_ptr<T[]>ile to możliwe, zamiast po prostu mówiąc: „nie można skopiować go”, a więc wybrać unique_ptr<T[]>, gdy nie chcesz kopii. Powstrzymanie kogoś przed zrobieniem czegoś złego niekoniecznie jest najważniejszym powodem wyboru klasy.
Nicol Bolas
8
std::vectorma więcej narzutów niż a std::unique_ptr- używa ~ 3 wskaźników zamiast ~ 1. std::unique_ptrblokuje kopiowanie konstrukcji, ale umożliwia przenoszenie konstrukcji, która jeśli semantycznie dane, z którymi pracujesz, mogą być tylko przenoszone, ale nie kopiowane, infekuje classzawierające je dane. Operacja na niepoprawnych danych faktycznie pogarsza klasę kontenera, a „po prostu nie używaj jej” nie zmywa wszystkich grzechów. Konieczność umieszczenia każdego wystąpienia std::vectorw klasie, w której ręcznie wyłączasz, movepowoduje ból głowy. std::unique_ptr<std::array>posiada size.
Jak - Adam Nevraumont
22

Scott Meyers ma to do powiedzenia w Effective Modern C ++

Istnienie std::unique_ptrna tablicach powinna być tylko odsetki intelektualnej do ciebie, ponieważ std::array, std::vector, std::stringsą praktycznie zawsze lepsze wybory struktura danych niż surowe tablic. O jedynej sytuacji, jaką mogę sobie wyobrazić, kiedy std::unique_ptr<T[]>miałoby sens, byłoby użycie interfejsu API podobnego do C, który zwraca surowy wskaźnik do tablicy sterty, którą przejmujesz na własność.

Myślę, że odpowiedź Charlesa Salvii jest istotna: std::unique_ptr<T[]>jest to jedyny sposób na zainicjowanie pustej tablicy, której rozmiar nie jest znany w czasie kompilacji. Co Scott Meyers miałby do powiedzenia na temat motywacji do korzystania std::unique_ptr<T[]>?

nowość
źródło
4
Wygląda na to, że po prostu nie przewidział kilku przypadków użycia, a mianowicie buforu o ustalonym rozmiarze, ale nieznanym w czasie kompilacji i / lub bufora, dla którego nie zezwalamy na kopiowanie. Istnieje również efektywność jako potencjalny powód, aby preferować go na vector stackoverflow.com/a/24852984/2436175 .
Antonio
17

W przeciwieństwie do std::vectori std::array, std::unique_ptrmoże posiadać wskaźnik NULL.
Jest to przydatne podczas pracy z interfejsami API języka C, które oczekują tablicy lub wartości NULL:

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());
}
Jerzy
źródło
10

Użyłem unique_ptr<char[]>do realizacji zdefiniowanej przez pule pamięci wykorzystywane w silniku gry. Chodzi o to, aby zapewnić używane wstępnie przydzielone pule pamięci zamiast dynamicznych przydziałów do zwracania wyników żądań kolizji i innych rzeczy, takich jak fizyka cząstek, bez konieczności przydzielania / zwalniania pamięci w każdej ramce. Jest to dość wygodne w tego rodzaju scenariuszach, w których potrzebujesz pul pamięci do przydzielania obiektów o ograniczonym czasie życia (zwykle jedna, 2 lub 3 klatki), które nie wymagają logiki niszczenia (tylko dezalokacja pamięci).

Simon Ferquel
źródło
9

W niektórych wywołaniach API Windows Win32 można znaleźć wspólny wzorzec , w którym użycie std::unique_ptr<T[]>może się przydać, np. Gdy nie wiesz dokładnie, jak duży powinien być bufor wyjściowy podczas wywoływania niektórych API Win32 (które zapisują niektóre dane w środku ten bufor):

// 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
źródło
Możesz po prostu użyć std::vector<char>w tych przypadkach.
Arthur Tacca
@ArthurTacca - ... jeśli nie masz nic przeciwko kompilatorowi inicjującemu każdy znak w buforze na 0 jeden po drugim.
TED,
9

Napotkałem przypadek, w którym musiałem użyć std::unique_ptr<bool[]>, który był w bibliotece HDF5 (biblioteka do wydajnego przechowywania danych binarnych, bardzo często używana w nauce). Niektóre kompilatory (w moim przypadku Visual Studio 2015) zapewniają kompresjęstd::vector<bool> (przy użyciu 8 booli w każdym bajcie), co jest katastrofą dla czegoś takiego jak HDF5, który nie dba o tę kompresję. Dzięki std::vector<bool>HDF5 w końcu odczytywał śmieci z powodu tej kompresji.

Zgadnij, kto był tam na ratunek, w przypadku, gdy std::vectornie działał, a ja musiałem przydzielić dynamiczną tablicę w czysty sposób? :-)

Fizyk kwantowy
źródło
9

W skrócie: jest to najbardziej wydajna pamięć.

A std::stringzawiera wskaźnik, długość i bufor „optymalizacji ciągu krótkiego”. Ale moja sytuacja polega na tym, że muszę przechowywać ciąg prawie zawsze pusty, w strukturze, w której mam setki tysięcy. W C po prostu bym użył char *i przez większość czasu byłby zerowy. Który działa również dla C ++, z wyjątkiem tego, że char *nie ma destruktora i nie wie, jak się usunąć. Natomiast a std::unique_ptr<char[]>usunie się, gdy wyjdzie poza zakres. Pusty std::stringzajmuje 32 bajty, ale pusty std::unique_ptr<char[]>zajmuje 8 bajtów, czyli dokładnie tyle, ile ma wskaźnik.

Największym minusem jest to, że za każdym razem, gdy chcę poznać długość struny, muszę ją wezwać strlen.

jorgbrown
źródło
3

Aby odpowiedzieć ludziom myślącym, że „musisz” użyć vectorzamiast unique_ptrmam przypadek w programowaniu CUDA na GPU, kiedy przydzielasz pamięć w urządzeniu, musisz wybrać tablicę wskaźników (z cudaMalloc). Następnie, podczas pobierania tych danych z hosta, musisz ponownie wybrać wskaźnik i możesz unique_ptrłatwo obsługiwać wskaźnik. Dodatkowy koszt konwersji double*na vector<double>jest zbędny i prowadzi do utraty perf.

Romain Laneuville
źródło
3

Dodatkowy powód, aby zezwolić i używać std::unique_ptr<T[]>, o którym do tej pory nie wspomniano w odpowiedziach: pozwala na zadeklarowanie w przód typu elementu tablicy.

Jest to przydatne, gdy chcesz zminimalizować łańcuch #include instrukcje w nagłówkach (aby zoptymalizować wydajność kompilacji).

Na przykład -

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

Dzięki powyższej strukturze kodu każdy może #include "myclass.h"i może korzystać MyClassbez konieczności uwzględniania wewnętrznych zależności implementacyjnych wymaganych przez MyClass::m_InternalArray.

Jeśli m_InternalArrayzamiast tego został zadeklarowany odpowiednio jako std::array<ALargeAndComplicatedClassWithLotsOfDependencies>, lub a std::vector<...>- wynikiem byłaby próba użycia niekompletnego typu, co jest błędem czasu kompilacji.

Boris Shpungin
źródło
W tym szczególnym przypadku wybrałbym wzorzec Pimpl w celu zerwania zależności - jeśli jest używany tylko prywatnie, wówczas definicję można odroczyć do momentu zaimplementowania metod klas; jeśli jest używany publicznie, użytkownicy klasy powinni już mieć konkretną wiedzę na ten temat class ALargeAndComplicatedClassWithLotsOfDependencies. Logicznie więc nie powinieneś napotykać takich scenariuszy.
3

Nie mogę wystarczająco mocno pogodzić się z duchem przyjętej odpowiedzi. „Narzędzie ostateczności”? Daleko stąd!

Według mnie jedną z najsilniejszych cech C ++ w porównaniu do C i niektórych innych podobnych języków jest zdolność do wyrażania ograniczeń, aby można je było sprawdzić w czasie kompilacji i zapobiec przypadkowemu niewłaściwemu użyciu. Podczas projektowania konstrukcji zadaj sobie pytanie, na jakie operacje powinna ona zezwalać. Wszelkie inne zastosowania powinny być zabronione, a najlepiej, jeśli takie ograniczenia można wdrożyć statycznie (w czasie kompilacji), aby niewłaściwe użycie skutkowało niepowodzeniem kompilacji.

Kiedy więc potrzebna jest tablica, odpowiedzi na następujące pytania określają jej zachowanie: 1. Czy jej rozmiar a) jest dynamiczny w czasie wykonywania lub b) statyczny, ale znany tylko w czasie wykonywania, lub c) statyczny i znany w czasie kompilacji? 2. Czy tablicę można przydzielić na stosie, czy nie?

I na podstawie odpowiedzi jest to, co uważam za najlepszą strukturę danych dla takiej tablicy:

       Dynamic     |   Runtime static   |         Static
Stack std::vector      unique_ptr<T[]>          std::array
Heap  std::vector      unique_ptr<T[]>     unique_ptr<std::array>

Tak, myślę, że unique_ptr<std::array>należy również wziąć pod uwagę, i żadne z nich nie jest narzędziem ostateczności. Pomyśl, co najlepiej pasuje do Twojego algorytmu.

Wszystkie z nich są kompatybilne ze zwykłymi interfejsami API języka C za pomocą surowego wskaźnika do tablicy danych ( vector.data()/ array.data()/ uniquePtr.get()).

PS Oprócz powyższych rozważań, istnieje również własność: std::arrayi std::vectormają semantykę wartości (mają natywne wsparcie dla kopiowania i przekazywania według wartości), podczas gdy unique_ptr<T[]>mogą być przenoszone tylko (wymusza pojedyncze prawo własności). Oba mogą być przydatne w różnych scenariuszach. Wręcz przeciwnie, zwykłe tablice statyczne ( int[N]) i zwykłe tablice dynamiczne ( new int[10]) nie oferują żadnego z nich i dlatego należy ich unikać, jeśli to możliwe - co powinno być możliwe w zdecydowanej większości przypadków. Jeśli to nie wystarczy, zwykłe tablice dynamiczne również nie oferują możliwości sprawdzenia ich wielkości - dodatkowa szansa na uszkodzenie pamięci i dziury w zabezpieczeniach.

Fioletowa Żyrafa
źródło
2

Mogą to być najodpowiedniejsze możliwe odpowiedzi, gdy można wcisnąć tylko jeden wskaźnik za pomocą istniejącego interfejsu API (pomyśl komunikat okna lub parametry wywołania zwrotnego związane z wątkami), które mają pewien pomiar czasu życia po „złapaniu” po drugiej stronie włazu, ale niezwiązane z kodem wywołującym:

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

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

Wszyscy chcemy, aby rzeczy były dla nas miłe. C ++ jest po raz drugi.

Simon Buchan
źródło
2

unique_ptr<char[]>może być używany tam, gdzie chcesz wydajności C i wygody C ++. Pomyśl, że musisz operować milionami (ok, miliardami, jeśli jeszcze nie ufasz) ciągów. Przechowywanie każdego z nich w osobnym stringlub vector<char>obiekcie byłoby katastrofą dla procedur zarządzania pamięcią (stertą). Zwłaszcza jeśli trzeba wielokrotnie przydzielać i usuwać różne ciągi.

Można jednak przydzielić jeden bufor do przechowywania tylu ciągów. Nie chciałbyś char* buffer = (char*)malloc(total_size);z oczywistych powodów (jeśli nie oczywiste, wyszukaj „dlaczego korzystać z inteligentnych plików ptrs”). Woliszunique_ptr<char[]> buffer(new char[total_size]);

Analogicznie te same względy dotyczące wydajności i wygody dotyczą nie- chardanych (rozważ miliony wektorów / macierzy / obiektów).

Serge Rogatch
źródło
Nie można umieścić ich wszystkich w jednym wielkim vector<char>? Przypuszczam, że odpowiedź jest taka, ponieważ będą one inicjowane zerem podczas tworzenia bufora, a nie będą, jeśli użyjesz unique_ptr<char[]>. Ale w Twojej odpowiedzi brakuje tego kluczowego samorodka.
Arthur Tacca
2
  • Ze względu na zgodność binarną potrzebujesz struktury zawierającej tylko wskaźnik.
  • Musisz połączyć się z interfejsem API, który zwraca przydzieloną pamięć new[]
  • W Twojej firmie lub projekcie obowiązuje ogólna zasada std::vector, na przykład, aby zapobiec przypadkowemu wprowadzeniu kopii przez nieostrożnych programistów
  • Chcesz uchronić nieostrożnych programistów przed przypadkowym wprowadzeniem kopii w tym przypadku.

Istnieje ogólna zasada, że ​​kontenery C ++ powinny być preferowane w porównaniu z własnymi wskaźnikami. Jest to ogólna zasada; ma wyjątki. Jest więcej; to tylko przykłady.

Jimmy Hartzell
źródło
0

Jeśli potrzebujesz dynamicznej tablicy obiektów, których nie da się zbudować z kopii, dobrym wyborem jest inteligentny wskaźnik do tablicy. Na przykład co jeśli potrzebujesz tablicy atomowej.

Ilia Minkin
źródło