std::unique_ptr
имеет поддержку массивов, например:
std::unique_ptr<int[]> p(new int[10]);
но нужно ли это? Вероятно, это более удобно для использования std::vector
или std::array
.
Нашли ли вы какую-либо пользу для этой конструкции?
std::unique_ptr
имеет поддержку массивов, например:
std::unique_ptr<int[]> p(new int[10]);
но нужно ли это? Вероятно, это более удобно для использования std::vector
или std::array
.
Нашли ли вы какую-либо пользу для этой конструкции?
std::shared_ptr<T[]>
, но должно быть, и, вероятно, будет в C ++ 14, если кто-то может быть обеспокоен, чтобы написать предложение. В то же время, всегда естьboost::shared_array
.std::shared_ptr
<T []> в C ++ 17 сейчас.Ответы:
Некоторые люди не могут себе позволить пользоваться
std::vector
даже распределителями. Некоторым людям нужен динамически изменяемый массив, поэтомуstd::array
его нет. И некоторые люди получают свои массивы из другого кода, который, как известно, возвращает массив; и этот код не будет переписан, чтобы вернутьvector
или что-то.Позволяя
unique_ptr<T[]>
, вы обслуживаете эти потребности.Короче говоря, вы используете,
unique_ptr<T[]>
когда вам нужно . Когда альтернативы просто не будут работать на вас. Это инструмент последней инстанции.источник
vector
». Вы можете утверждать, являются ли это разумными требованиями или нет, но вы не можете отрицать, что они существуют .std::vector
если сможетstd::unique_ptr
.unique_ptr
но такие проекты действительно существуют.Есть компромиссы, и вы выбираете решение, которое соответствует тому, что вы хотите. С верхней части моей головы:
Начальный размер
vector
иunique_ptr<T[]>
позвольте размеру быть определенным во время выполненияarray
только позволяет указать размер во время компиляцииИзменение размера
array
иunique_ptr<T[]>
не разрешать изменение размераvector
делаетМесто хранения
vector
иunique_ptr<T[]>
хранить данные вне объекта (обычно в куче)array
хранит данные непосредственно в объектеКопирование
array
иvector
разрешить копированиеunique_ptr<T[]>
не позволяет копироватьОбмен / ход
vector
иunique_ptr<T[]>
иметь O (1) времяswap
и операции перемещенияarray
имеет O (n) времяswap
и операции перемещения, где n - количество элементов в массивеНедействительный указатель / ссылка / итератор
array
гарантирует, что указатели, ссылки и итераторы никогда не будут признаны недействительными, пока объект активен, даже еслиswap()
unique_ptr<T[]>
не имеет итераторов; указатели и ссылки становятся недействительными, только еслиswap()
объект активен. (После замены указатели указывают на массив, с которым вы меняли местами, поэтому они все еще «действительны» в этом смысле.)vector
может аннулировать указатели, ссылки и итераторы при любом перераспределении (и предоставляет некоторые гарантии, что перераспределение может произойти только при определенных операциях).Совместимость с концепциями и алгоритмами
array
иvector
оба контейнераunique_ptr<T[]>
это не контейнерЯ должен признать, что это похоже на возможность некоторого рефакторинга с дизайном на основе политик.
источник
vector
. Затем вы увеличиваете размер или емкостьvector
такого, что оно вызывает перераспределение. Тогда этот итератор, указатель или ссылка больше не будут указывать на этот элементvector
. Это то, что мы подразумеваем под «недействительностью». С этой проблемой не бываетarray
, потому что нет «перераспределения». На самом деле, я просто заметил эту деталь и отредактировал ее так, чтобы она подходила.unique_ptr<T[]>
потому, что перераспределения не существует. Но, конечно, когда массив выходит из области видимости, указатели на определенные элементы все равно будут недействительными.T[]
, размер (или эквивалентная информация) должен где-то зависать,operator delete[]
чтобы правильно уничтожить элементы массива. Было бы хорошо, если бы у программиста был к этому доступ.Одна из причин, по которой вы можете использовать a,
unique_ptr
заключается в том, что вы не хотите оплачивать стоимость выполнения инициализации массива.std::vector
Конструктор иstd::vector::resize()
оценит инициализируетT
- ноnew
не будет делать, еслиT
это POD.Смотрите Value-Initialized Objects в C ++ 11 и конструктор std :: vector
Обратите внимание, что
vector::reserve
здесь нет альтернативы: доступ к необработанному указателю после std :: vector :: reserve безопасен?Это та же причина , программист C может выбрать
malloc
болееcalloc
.источник
std::vector
предоставить пользовательскому распределителю, который избегает создания типовstd::is_trivially_default_constructible
и уничтожения объектовstd::is_trivially_destructible
, хотя это строго нарушает стандарт C ++ (поскольку такие типы не инициализируются по умолчанию).std::unique_ptr
не предоставляет никакой связанной проверки вопреки многимstd::vector
реализациям.std::vector
Стандарт требует проверки границ.at()
. Полагаю, вы имели в виду, что в некоторых реализациях есть режимы отладки, которые.operator[]
тоже регистрируются, но я считаю, что это бесполезно для написания хорошего переносимого кода.std::vector
Могут быть скопированы вокруг, в то время какunique_ptr<int[]>
позволяет выразить уникальное владение массива.std::array
с другой стороны, требует, чтобы размер был определен во время компиляции, что может быть невозможно в некоторых ситуациях.источник
unique_ptr
вместоshared_ptr
. Я что-то упускаю?unique_ptr
делает больше, чем просто предотвращает случайное неправильное использование. Это также меньше и меньше, чем накладные расходыshared_ptr
. Дело в том, что хотя в классе и есть семантика, предотвращающая «неправильное использование», это не единственная причина, по которой следует использовать определенный тип. Иvector
гораздо более полезен в качестве хранилища массивов, чемunique_ptr<T[]>
, если только по той причине, что он имеет размер .vector
более ,unique_ptr<T[]>
где это возможно, вместо того , чтобы просто сказать, «вы не можете копировать» и , следовательно , выбрать ,unique_ptr<T[]>
когда вы не хотите копии. Удержание кого-либо от неправильных действий не обязательно является самой важной причиной выбора класса.std::vector
имеет больше накладных расходов, чемstd::unique_ptr
- он использует ~ 3 указателя вместо ~ 1.std::unique_ptr
блокирует построение копирования, но разрешает построение перемещения, которое, если семантически данные, с которыми вы работаете, можно только перемещать, но не копировать, заражаетclass
содержащие данные. Имея операцию над данными, не действует на самом деле делает ваш класс контейнера хуже, и «просто не использовать его» не смывает все грехи. Необходимость помещать каждый свой экземплярstd::vector
в класс, где вы отключаете вручную,move
- головная боль.std::unique_ptr<std::array>
имеетsize
.Скотт Мейерс говорит об этом в Effective Modern C ++
Я думаю, что ответ Чарльза Сальвии важен: это
std::unique_ptr<T[]>
единственный способ инициализировать пустой массив, размер которого неизвестен во время компиляции. Что бы сказал Скотт Мейерс об этой мотивации для использованияstd::unique_ptr<T[]>
?источник
vector
stackoverflow.com/a/24852984/2436175 .В отличие от
std::vector
иstd::array
,std::unique_ptr
может иметь нулевой указатель.Это удобно при работе с C API, которые ожидают либо массив, либо NULL:
источник
Я использовал
unique_ptr<char[]>
для реализации предварительно выделенных пулов памяти, используемых в игровом движке. Идея состоит в том, чтобы предоставить предварительно распределенные пулы памяти, используемые вместо динамических распределений для возврата результатов запросов на столкновения и других вещей, таких как физика элементарных частиц, без необходимости выделять / освобождать память в каждом кадре. Это довольно удобно для таких сценариев, когда вам нужны пулы памяти для выделения объектов с ограниченным сроком службы (обычно один, 2 или 3 кадра), которые не требуют логики уничтожения (только освобождение памяти).источник
Общий шаблон можно найти в некоторых вызовах Windows Win32 API , в которых использование
std::unique_ptr<T[]>
может оказаться полезным, например, когда вы точно не знаете, насколько большим должен быть выходной буфер при вызове некоторого Win32 API (который будет записывать некоторые данные внутри этот буфер):источник
std::vector<char>
в этих случаях.Я столкнулся со случаем, когда мне пришлось использовать
std::unique_ptr<bool[]>
, который был в библиотеке HDF5 (библиотека для эффективного хранения двоичных данных, много использованная в науке). Некоторые компиляторы (в моем случае Visual Studio 2015) обеспечивают сжатиеstd::vector<bool>
(используя 8 bools в каждом байте), что является катастрофой для чего-то вроде HDF5, который не заботится об этом сжатии. При этомstd::vector<bool>
HDF5 в конечном итоге считывал мусор из-за этого сжатия.Угадайте, кто был там для спасения, в случае, когда
std::vector
не сработало, и мне нужно было аккуратно выделить динамический массив? :-)источник
В двух словах: это наиболее эффективная память.
A
std::string
поставляется с указателем, длиной и буфером «оптимизации коротких строк». Но в моей ситуации мне нужно хранить строку, которая почти всегда пуста, в структуре, которой у меня сотни тысяч. В С я бы просто использовалchar *
, и большую часть времени он был бы нулевым. Это также работает для C ++, за исключением того, что achar *
не имеет деструктора и не знает, как удалить себя. Напротив, astd::unique_ptr<char[]>
удалит себя, когда выйдет из области видимости. Пустойstd::string
занимает 32 байта, но пустойstd::unique_ptr<char[]>
занимает 8 байтов, то есть точно размер его указателя.Самым большим недостатком является то, что каждый раз, когда я хочу узнать длину строки, мне приходится обращаться
strlen
к ней.источник
Чтобы ответить тем, кто думает, что вы «должны» использовать
vector
вместо «unique_ptr
У меня есть случай программирования CUDA на GPU», когда вы выделяете память в Device, вы должны использовать массив указателей (сcudaMalloc
). Затем, при получении этих данных в Host, вы должны снова пойти за указателем, иunique_ptr
вы можете легко справиться с указателем. За дополнительной платой преобразованияdouble*
вvector<double>
ненужно и приводит к потере перфорации.источник
Еще одна причина разрешить и использовать
std::unique_ptr<T[]>
, которая не была упомянута в ответах до сих пор: она позволяет вам заранее объявить тип элемента массива.Это полезно, когда вы хотите минимизировать связанные
#include
операторы в заголовках (чтобы оптимизировать производительность сборки).Например -
С вышеупомянутой структурой кода любой может
#include "myclass.h"
и может использоватьMyClass
, не включая внутренние зависимости реализации, требуемыеMyClass::m_InternalArray
.Если
m_InternalArray
вместо этого было объявлено как astd::array<ALargeAndComplicatedClassWithLotsOfDependencies>
или astd::vector<...>
, соответственно - результатом будет попытка использования неполного типа, что является ошибкой во время компиляции.источник
class ALargeAndComplicatedClassWithLotsOfDependencies
. Логично, что вы не должны сталкиваться с такими сценариями.Я не могу не согласиться с духом принятого ответа достаточно сильно. «Инструмент последней инстанции»? Отнюдь не!
На мой взгляд, одна из самых сильных возможностей C ++ по сравнению с C и некоторыми другими подобными языками - это способность выражать ограничения, чтобы их можно было проверять во время компиляции и предотвратить случайное неправильное использование. Поэтому при проектировании структуры спросите себя, какие операции она должна разрешать. Все другие виды использования должны быть запрещены, и лучше всего, если такие ограничения могут быть реализованы статически (во время компиляции), чтобы неправильное использование приводило к ошибке компиляции.
Поэтому, когда нужен массив, ответы на следующие вопросы определяют его поведение: 1. Является ли его размер a) динамическим во время выполнения, или b) статическим, но известным только во время выполнения, или c) статичным и известным во время компиляции? 2. Можно ли разместить массив в стеке или нет?
И, исходя из ответов, это то, что я считаю лучшей структурой данных для такого массива:
Да, я думаю, что
unique_ptr<std::array>
также следует учитывать, и ни один из них не является последним средством. Подумайте, что лучше всего подходит для вашего алгоритма.Все они совместимы с простыми API C через необработанный указатель на массив данных (
vector.data()
/array.data()
/uniquePtr.get()
).PS Помимо вышеизложенных соображений, есть и право собственности:
std::array
и ониstd::vector
имеют семантику значения (имеют встроенную поддержку копирования и передачи по значению), ноunique_ptr<T[]>
могут быть перемещены только (обеспечивает единоличное владение). Любой может быть полезен в различных сценариях. Напротив, простые статические массивы (int[N]
) и простые динамические массивы (new int[10]
) не предлагают ни одного, и поэтому их следует избегать, если это возможно, - что должно быть возможно в подавляющем большинстве случаев. Если этого было недостаточно, обычные динамические массивы также не дают возможности запрашивать их размер - дополнительная возможность для повреждений памяти и дыр в безопасности.источник
Они могут быть самым правильным ответом из всех возможных, когда вы получаете только один указатель через существующий API (параметры окна сообщения или параметры обратного вызова, связанного с потоками), которые имеют некоторую меру времени жизни после того, как их «поймали» на другой стороне штриховки, но который не связан с вызывающим кодом:
Мы все хотим, чтобы все было хорошо для нас. С ++ для другого времени.
источник
unique_ptr<char[]>
можно использовать там, где вы хотите производительность C и удобство C ++. Учтите, что вам нужно оперировать миллионами (ну, миллиардами, если вы еще не доверяете) строк. Хранение каждого из них в отдельном объектеstring
илиvector<char>
объекте было бы катастрофой для процедур управления памятью (кучей). Особенно, если вам нужно выделить и удалить разные строки много раз.Однако вы можете выделить один буфер для хранения такого количества строк. Вам не понравится
char* buffer = (char*)malloc(total_size);
по понятным причинам (если не очевидно, поиск «зачем использовать умные ptrs»). Вы бы предпочлиunique_ptr<char[]> buffer(new char[total_size]);
По аналогии, те же соображения производительности и удобства применимы к не-
char
данным (рассмотрим миллионы векторов / матриц / объектов).источник
vector<char>
? Ответ, я полагаю, заключается в том, что они будут инициализироваться нулем при создании буфера, а не будут, если вы используетеunique_ptr<char[]>
. Но этот ключевой самородок отсутствует в вашем ответе.new[]
std::vector
, запрещающее использование , например, предотвращения случайного введения копий неосторожными программистами.Существует общее правило, что контейнеры C ++ предпочтительнее, чем использование собственных указателей. Это общее правило; у него есть исключения. Есть еще кое-что; это всего лишь примеры.
источник
Если вам нужен динамический массив объектов, которые не поддаются копированию, то разумный указатель на массив - это путь. Например, что если вам нужен массив атомиков.
источник