В std::unique_ptr
есть поддержка массивов, например:
std::unique_ptr<int[]> p(new int[10]);
но нужна ли она? Возможно, удобнее использовать std::vector
или std::array
.
Находите ли вы применение этой конструкции?
Некоторые люди не могут позволить себе использовать std::vector
, даже с аллокаторами. Некоторым нужен динамически изменяемый размер массива, поэтому std::array
не подходит. А некоторые люди получают свои массивы из другого кода, который, как известно, возвращает массив; и этот код не будет переписан для возврата vector
или чего-то подобного.
Разрешая unique_ptr<T[]>
, вы удовлетворяете эти потребности.
Короче говоря, вы используете unique_ptr<T[]>
, когда вам это нужно. Когда альтернативные варианты просто не подходят. Это инструмент последнего средства.
Есть компромиссные варианты, а вы выбираете решение, которое соответствует тому, что вы хотите. С верхней части моей головы:
Начальный размер
вектор
и unique_ptr не<Т[]>
чтобы размер будет определен во время выполнениямассив
только позволяет размер будет определен во время компиляцииИзменение размера
время
и unique_ptr не<Т[]>
не допустить изменения размераХранение
вектор
и unique_ptr не<Т[]>
хранить данные за пределами объекта (обычно в куче)массив
хранит данные непосредственно в объектеКопирование
массив
и вектор
разрешить копированиеЗамена/перемещение
вектор
и unique_ptr не<Т[]>
за O(1) времени своп
и ход операциимассива
имеет сложность o(n) времени своп
и ход операций, где n-количество элементов в массивеУказатель/ссылку/итератор недействительным
массив
обеспечивает указатели, ссылки и итераторы никогда не будут аннулированы, пока объект жив, даже на своп()
unique_ptr не<Т[]>
не имеет итераторы; указатели и ссылки являются лишь законной силы своп()
пока объект жив. (После замены, указатели точку в массив, который вы заменили, так они'вновь еще "не действует" в этом смысле.)Совместимость с понятий и алгоритмов
время
и вектор
оба контейнерыunique_ptr не<Т[]>
это не контейнерЯ должен признать, это выглядит как возможность для небольшого рефакторинга с учетом политики дизайна.
Одна из причин, вы могли бы использовать `unique_ptr не является если вы Don'т хотите оплатить стоимость выполнения значение-инициализация массива.
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars
std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
В СТД::векторконструктор
СТД::вектор::размер () будет значение-инициализировать т
- но "новые" не будут этого делать, если Т
представляет собой стручок.
Обратите внимание, что `вектор::заповедник-это не альтернатива здесь: https://stackoverflow.com/questions/8228072/is-accessing-the-raw-pointer-after-stdvectorreserve-safe
Это'с той же причине C программист может выбрать функция malloc
за памятью
.
Вектор std::vector
можно копировать, а unique_ptr<int[]>
позволяет выразить уникальное владение массивом. С другой стороны, std::array
требует определения размера во время компиляции, что может быть невозможно в некоторых ситуациях.
Скотт Мейерс рассказывает в эффективный современный c++
существование
с std::unique_ptr не для массивов должны быть только интеллектуальный интерес к вам, потому что
с std::массив,
СТД::вектор,
СТД::строкапрактически всегда лучше, выбор структуры данных, чем сырые массивы. О единственной ситуации я могу понять, когда
с std::unique_ptr не<Т[]>` имело бы смысл будет, когда вы're с помощью СИ-подобного API, который возвращает сырой указатель на куче массива, вы берете на себя право собственности.
Я думаю, что Чарльз сальвии'ы ответьте хотя актуален:, что с std::unique_ptr не<Т[]>
это единственный способ, чтобы инициализировать пустой массив, чей размер не известен во время компиляции. Что бы Скотт Мейерс нужно сказать о такой мотивации, используя с std::unique_ptr не<Т[]>
?
Вопреки СТД::вектори
с std::массив,
с std::unique_ptr не может иметь нулевой указатель.
Это пригодится при работе с API-интерфейсы c, ожидающие или массив или 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());
}
Я использовал unique_ptr<char[]>
для реализации пулов предварительно распределенной памяти, используемых в игровом движке. Идея заключается в том, чтобы предоставить предварительно распределенные пулы памяти, используемые вместо динамического распределения для возврата результатов запросов столкновений и других вещей, таких как физика частиц, без необходимости выделять / освобождать память в каждом кадре. Это довольно удобно для такого рода сценариев, где вам нужны пулы памяти для размещения объектов с ограниченным временем жизни (обычно один, 2 или 3 кадра), которые не требуют логики уничтожения (только деаллокация памяти).
В двух словах: это'ы на сегодняшний день наиболее эффективный для памяти.
В СТД::строкапоставляется с указателем, длинну, и на "короткие строки-оптимизация и" буфера. Но моя ситуация мне нужно сохранить строку, которая почти всегда пустая, в структуру, которая у меня сотни тысяч. В C, я бы просто использовать
char и, и было бы нуль большую часть времени. Который работает для C++, тоже, за исключением того, что
char и не имеет деструктора, а не'т знаем, чтобы удалить себя. Напротив,
с std::unique_ptr не<символ[]>будет удалить себя, когда он выходит из области видимости. Пустой
СТД::строказанимает 32 байта, но пустой
с std::unique_ptr не<символ[]>` занимает 8 байт, то есть ровно на размер указателя.
Самый большой недостаток в том, что каждый раз, когда я хочу знать длину строки, Мне нужно позвонить функция strlen
на нем.
Общий шаблон может быть найден в [некоторых][GetPackageApplicationIds_MSDN] ОС Windows API-интерфейса Win32 звонки, в которых использование с std::unique_ptr не<Т[]>
может пригодиться, например, когда вы Don'т знаю, насколько большой буфер вывода должен быть при вызове некоторые Win32 API (так что буду писать некоторые данные в буфер):
// 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...
...
[GetPackageApplicationIds_MSDN]: http://msdn.microsoft.com/en-us/library/windows/desktop/dn270603(В=и 85).аспн
Я столкнулся с такой случай, когда я должен был использовать с std::unique_ptr не<типа bool [] и GT;
, который был в HDF5 библиотека (библиотека для эффективное бинарное хранение данных, используемый много в науке). Некоторые компиляторы (в Visual Studio 2015 в моем случае) обеспечивают сжатие СТД::вектор<типа bool и GT;
(с помощью 8 значений в каждый байт), что является катастрофой для что-то вроде HDF5, который не'т волнует, что сжатие. С СТД::вектор<типа bool и GT;`, HDF5 был в конце концов читать мусор из-за этого сжатия.
Угадайте, кто был там для спасения, в случае, когда СТД::вектор` ничего't работа, и мне нужно выделить динамический массив чисто? :-)
Еще одна причина, чтобы разрешить и использовать с std::unique_ptr не<Т [] и GT;`, что еще'т было упомянуто в ответах до сих пор: она позволяет вперед-объявить тип элемента массива.
Это полезно, когда вы хотите минимизировать прикованный #включить
высказывания в заголовках (оптимизировать производительность построения.)
Например
класса MyClass.ч:
class ALargeAndComplicatedClassWithLotsOfDependencies;
class MyClass {
...
private:
std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};
myclass.cpp:
#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"
// MyClass implementation goes here
С помощью данной структуры кода, кто может #включить и"класса MyClass.ч"
В и использовать класса MyClass
, без включения внутренних зависимостей, необходимых для осуществления класса MyClass::m_InternalArray
.
Если m_InternalArrayбыл объявлен как
с std::массив, или
СТД::вектор<...>`, соответственно - результат был бы покушение на использование неполного типа, что ошибка времени компиляции.
Чтобы ответить на людей, думая, что вы "не надо" и использовать вектор
вместо unique_ptr не
у меня есть дело в программирования CUDA на GPU при выделении памяти в устройство необходимо зайти на массив указателей (с cudaMalloc). Затем, при получении этих данных в хост, нужно идти снова на указатель и
unique_ptr неэто хорошо, чтобы легко справиться указатель. Дополнительных затрат на преобразование двойных*
в вектор<двойной>
является ненужным и приводит к потере производительности.
СТД::вектор
, например, для предотвращения неосторожного программиста от случайного представляем копииЕсть общее правило, что c++ контейнеры должны быть предпочтительнее, чем прокатки свой собственный с указателями. Это общее правило, которое не имеет исключений. Там's больше, это лишь примеры.
Они могут быть верным ответ можно получить только ткнуть указателем через существующий API (думаю окне сообщений или потоков, связанных с обратного вызова, параметры), которые имеют определенную продолжительность жизни после того, как "поймали" на другую сторону люка, но который никакого отношения к вызывающему коду:
unique_ptr<byte[]> data = get_some_data();
threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
data.release());
Мы все хотим, чтобы все было хорошо для нас. C++ - это для другого раза.
unique_ptr не<символ[]>
может быть использована там, где требуется производительность и удобство работы с C++. Считаю нужно работать на миллионы (если не миллиарды, если вы Don'т доверять еще) строк. Хранение каждого из них в отдельную строку или вектор<чар>` объект станет катастрофой для памяти (кучи) режимы управления. Особенно, если вам нужно выделить и удалить разные строки много раз.
Тем не менее, можно выделить один буфер для хранения, что много строк. Вы бы'т, как типа char* буфер = (тип char*)Танос(total_size);
по понятным причинам (если не очевидно, поиск по "Почему использовать смарт-ПТРС-то"). Вы бы как unique_ptr не<типа char[]> буфер(новая Чаре[total_size]);
По аналогии, такую же производительность&соображения удобства применяется для данных типа char`(считают миллионы векторов/матриц/объекты).
Если вам нужен динамический массив объектов, которые не копируют-конструктивны, то умный указатель на массив является способом пойти. Например, что если вам нужен массив атомной и Л;int>В С.