В нашем курсе C++ они предлагают больше не использовать массивы C++ в новых проектах. Насколько мне известно, сам Страуструп предлагает не использовать массивы. Но есть ли существенные различия в производительности?
Потому что обычно с лучшей функциональностью приходит худшая производительность.
Я согласен с преждевременной оптимизацией, но выбор лучшего метода хранения имеет большой смысл. Часто в реальном мире необходимо отправить код и разработать следующий продукт, а этап оптимизации не выполняется.
Желаю, чтобы люди перестали кричать "преждевременная оптимизация!" всякий раз, когда кто-то задает простой вопрос, связанный с производительностью! ответьте на вопрос, а не просто ПРЕДВАРИТЕЛЬНО предполагайте, что люди что-то делают преждевременно.
@ d7samaurai: согласен, я еще не видел, чтобы кто-нибудь пробовал использовать int main(int argc, const std::vector<string>& argv)
@MarkKCowan Для общего кода это явно преждевременная оптимизация!
@tunnuz с огромной силой приносит большую ответственность





STL - это сильно оптимизированная библиотека. Фактически, даже предлагается использовать STL в играх, где может потребоваться высокая производительность. Массивы слишком подвержены ошибкам, чтобы их можно было использовать в повседневных задачах. Современные компиляторы также очень умны и действительно могут создавать отличный код с помощью STL. Если вы знаете, что делаете, STL обычно может обеспечить необходимую производительность. Например, инициализируя векторы до требуемого размера (если вы знаете это с самого начала), вы можете в основном добиться производительности массива. Однако могут быть случаи, когда вам по-прежнему нужны массивы. При взаимодействии с низкоуровневым кодом (например, сборкой) или старыми библиотеками, которым требуются массивы, вы, возможно, не сможете использовать векторы.
учитывая, что вектор непрерывен, все еще довольно легко взаимодействовать с библиотеками, которым требуются массивы.
Да, но если вы хотите возиться с внутренним содержимым вектора, использование вектора было бы меньшим преимуществом. Кстати, ключевое слово было «не мог бы».
я знаю только один случай, когда нельзя использовать векторы: если размер равен 0. тогда & a [0] или & * a.begin () не будут работать. C++ 1x исправит это, введя функцию a.data (), которая возвращает внутренний буфер, сохраняющий элементы
Когда я писал о конкретном сценарии, я думал, что это массивы на основе стека.
Взаимодействие с вектором или любым смежным контейнером с C: vec.data() для данных и vec.size() для размера. Это так просто.
В наши дни это не так. См. Доклад Майка Эктона «Дизайн, ориентированный на данные»
Следует избегать использования массивов C++ с new (то есть использования динамических массивов). Проблема заключается в том, что вам нужно отслеживать размер, и вам нужно удалить их вручную и выполнить все виды уборки.
Использование массивов в стеке также не рекомендуется, потому что у вас нет проверки диапазона, а передача массива приведет к потере любой информации о его размере (преобразование массива в указатель). В этом случае вам следует использовать boost::array, который объединяет массив C++ в небольшой класс и предоставляет функцию size и итераторы для итерации по нему.
Теперь std :: vector против собственных массивов C++ (взято из интернета):
// Comparison of assembly code generated for basic indexing, dereferencing,
// and increment operations on vectors and arrays/pointers.
// Assembly code was generated by gcc 4.1.0 invoked with g++ -O3 -S on a
// x86_64-suse-linux machine.
#include <vector>
struct S
{
int padding;
std::vector<int> v;
int * p;
std::vector<int>::iterator i;
};
int pointer_index (S & s) { return s.p[3]; }
// movq 32(%rdi), %rax
// movl 12(%rax), %eax
// ret
int vector_index (S & s) { return s.v[3]; }
// movq 8(%rdi), %rax
// movl 12(%rax), %eax
// ret
// Conclusion: Indexing a vector is the same damn thing as indexing a pointer.
int pointer_deref (S & s) { return *s.p; }
// movq 32(%rdi), %rax
// movl (%rax), %eax
// ret
int iterator_deref (S & s) { return *s.i; }
// movq 40(%rdi), %rax
// movl (%rax), %eax
// ret
// Conclusion: Dereferencing a vector iterator is the same damn thing
// as dereferencing a pointer.
void pointer_increment (S & s) { ++s.p; }
// addq $4, 32(%rdi)
// ret
void iterator_increment (S & s) { ++s.i; }
// addq $4, 40(%rdi)
// ret
// Conclusion: Incrementing a vector iterator is the same damn thing as
// incrementing a pointer.
Примечание.Если вы выделяете массивы с помощью new и выделяете объекты, не являющиеся классами (например, обычный int) или классы без определенного пользователем конструктора а также, вы не хотите, чтобы ваши элементы изначально инициализировались, использование массивов, выделенных new, может иметь преимущества в производительности, потому что std::vector инициализирует все элементы значениями по умолчанию (например, 0 для int) при построении (спасибо @bernie за напоминание мне).
Кто изобрел чертов синтаксис AT&T? Только если бы я знал ... :)
Обратите внимание, что std :: tr1 :: array (или boost :: array) может разрешить случаи, когда вы использовали бы собственный массив с new.
Это неверно для компилятора Visual C++. Но для GCC это так.
Суть моего ответа в том, что вектор не имеют медленнее, чем соответствующие операции с указателем. Конечно, это может быть (легко добиться, включив также режим отладки) :)
+1 за «Индексирование вектора - это то же самое, что индексирование указателя»., а также за другие выводы.
хорошо, это происходит немного поздно, но все же: с моей стороны нет проблем с синтаксисом AT&T - исходя из старого языка ассемблера 68k, мне показалось намного более естественным использовать синтаксис move from, to
@ Piotr99 Я не собираюсь с вами спорить, но когда вы изучаете сборку после, изучая языки более высокого уровня, синтаксис Intel имеет гораздо больше смысла, чем некоторые обратные, префиксы (числа), суффиксы (инструкции) и неясные (доступ к памяти) ) характер синтаксиса AT&T.
Это сравнение производительности не принимает во внимание первоначальное распределение std::vector по сравнению с массивом. Подробнее см. Мой ответ: stackoverflow.com/a/43955169/1030527
@bernie в вопросе не указано, как распределяется массив. Я не могу сравнить исходную константу распределения std::vector с чем-то, что не указано. Если массив размещен в стеке, очевидно, что стоимость размещения std::vector выше. Но если массив выделен с помощью new, я не думаю, что будет иметь значение, что вы используете.
@bernie Я вижу, вы имеете в виду проблему инициализации. я добавил абзац к ответу
Я не думаю, что следует препятствовать использованию динамических массивов только потому, что люди должны научиться пользоваться delete. Я имею в виду, что так оно и есть. Это не означает, что функция плохая или опасная, это просто означает, что программист должен знать, как на самом деле программировать.
Если вам не нужно динамически регулировать размер, у вас есть накладные расходы памяти на сохранение емкости (один указатель / size_t). Вот и все.
Выбирайте STL. Нет никаких штрафов за производительность. Алгоритмы очень эффективны и хорошо справляются с такими деталями, о которых большинство из нас даже не задумывается.
Разница в производительности между ними очень сильно зависит от реализации - если вы сравните плохо реализованный std :: vector с оптимальной реализацией массива, массив выиграет, но развернет его, и вектор выиграет ...
Пока вы сравниваете яблоки с яблоками (либо массив, и вектор имеют фиксированное количество элементов, либо оба меняют размер динамически), я бы подумал, что разница в производительности незначительна, если вы следуете полученной практике кодирования STL. Не забывайте, что использование стандартных контейнеров C++ также позволяет вам использовать предварительно свернутые алгоритмы, которые являются частью стандартной библиотеки C++, и большинство из них, вероятно, будут более эффективными, чем средняя реализация того же алгоритма, который вы создаете самостоятельно. .
Тем не менее, IMHO вектор выигрывает в сценарии отладки с отладочным STL, поскольку большинство реализаций STL с правильным режимом отладки могут, по крайней мере, выделить / cathc типичные ошибки, совершаемые людьми при работе со стандартными контейнерами.
О, и не забывайте, что массив и вектор используют одну и ту же структуру памяти, поэтому вы можете использовать векторы для передачи данных в устаревший код C или C++, который ожидает базовые массивы. Однако имейте в виду, что большинство ставок в этом сценарии неверно, и вы снова имеете дело с необработанной памятью.
Я думаю, что для удовлетворения требований к производительности (поиск и вставка O (1)) вы почти имеют реализуете std :: vector <> с использованием динамических массивов. Конечно, это очевидный способ сделать это.
Не только требования к производительности, но и требование непрерывности хранилища. Плохая реализация вектора создаст слишком много уровней косвенного обращения между массивом и API. Хорошая векторная реализация позволит использовать встроенный код, SIMD в циклах и т. д.
Плохая реализация вектора, как описано, не будет соответствовать стандарту. Если вы хотите косвенного обращения, можно использовать std::deque.
Чтобы ответить на что-то, Mehrdad сказал:
However, there might be cases where you still need arrays. When interfacing with low level code (i.e. assembly) or old libraries that require arrays, you might not be able to use vectors.
Совсем неправда. Векторы красиво превращаются в массивы / указатели, если вы используете:
vector<double> vector;
vector.push_back(42);
double *array = &(*vector.begin());
// pass the array to whatever low-level code you have
Это работает для всех основных реализаций STL. В следующем стандарте он должен будет работать (хотя сегодня он работает нормально).
как вы думаете, почему сейчас не нужно работать? & * v.begin () даст вам указатель на начало внутреннего буфера вектора, и он будет смежным. что изменится, так это std :: string, которая должна быть непрерывной (по крайней мере, так мне сказал ppl)
Текущий стандарт ничего такого не говорит. Это подразумевается, и это реализовано как непрерывное хранилище. Но стандарт просто говорит, что это контейнер с произвольным доступом (с использованием итераторов). Следующий стандарт будет явным.
& * v.begin () просто применяет оператор & к результату разыменования итератора. При разыменовании может возвращаться ЛЮБОЙ тип. Использование оператора address-of может снова вернуть ЛЮБОЙ тип. Стандарт не определяет это как указатель на непрерывную область памяти.
Исходный текст Стандарта 1998 года действительно не требовал этого, но в 2003 году было добавление, посвященное этому вопросу, так что он действительно охвачен Стандартом. grassutter.wordpress.com/2008/04/07/…
+1 за комментарий Немани. Кроме того, в течение некоторого времени это в основном академические дебаты; все современные stl-библиотеки какое-то время «поступали правильно».
C++ 03 явно говорит, что &v[n] == &v[0] + n действителен, если n находится в пределах диапазона размеров. Абзац, содержащий этот оператор, не изменился в C++ 11.
@FrankKrueger: Что делать, если у вас есть vector<vector<double>>? Как можно указать указателем?
почему бы просто не использовать std :: vector :: data ()?
А как насчет другого пути? Учитывая указатель из низкоуровневого кода (или C-Export DLL), вы не сможете обернуть вектор вокруг него без копирования.
Может быть какой-то крайний случай, когда у вас есть доступ к вектору внутри встроенной функции внутри встроенной функции, когда вы вышли за рамки того, что компилятор будет встроить, и он вызовет вызов функции. Это будет настолько редко, что не стоит беспокоиться - в целом я согласен с литб.
Я удивлен, что никто об этом еще не упомянул - не беспокойтесь о производительности, пока не будет доказано, что это проблема, а затем выполните тест.
Иногда массивы действительно лучше векторов. Если вы всегда манипулируете набор объектов фиксированной длины, лучше массивы. Рассмотрим следующие фрагменты кода:
int main() {
int v[3];
v[0]=1; v[1]=2;v[2]=3;
int sum;
int starttime=time(NULL);
cout << starttime << endl;
for (int i=0;i<50000;i++)
for (int j=0;j<10000;j++) {
X x(v);
sum+=x.first();
}
int endtime=time(NULL);
cout << endtime << endl;
cout << endtime - starttime << endl;
}
где векторная версия X есть
class X {
vector<int> vec;
public:
X(const vector<int>& v) {vec = v;}
int first() { return vec[0];}
};
а версия X в виде массива:
class X {
int f[3];
public:
X(int a[]) {f[0]=a[0]; f[1]=a[1];f[2]=a[2];}
int first() { return f[0];}
};
Версия массива main () будет быстрее, потому что мы избегаем накладные расходы «нового» каждый раз во внутреннем цикле.
(Этот код был опубликован мной на comp.lang.C++).
Я бы сказал, что главная проблема не в производительности, а в безопасности. Вы можете сделать много ошибок с массивами (например, подумайте об изменении размера), когда вектор избавит вас от лишней боли.
Векторы - это массивы под капотом. Спектакль такой же.
Единственное место, где вы можете столкнуться с проблемой производительности, - это неправильный размер вектора с самого начала.
По мере заполнения вектора он изменит свой размер, и это может означать новое выделение массива, за которым следуют n конструкторов копирования, за которыми следуют примерно n вызовов деструктора, за которыми следует удаление массива.
Если ваша конструкция / уничтожение обходится дорого, вам гораздо лучше для начала сделать вектор правильного размера.
Есть простой способ продемонстрировать это. Создайте простой класс, который показывает, когда он построен / уничтожен / скопирован / назначен. Создайте вектор этих вещей и начните нажимать их на задний конец вектора. Когда вектор заполнится, произойдет каскад действий по мере изменения размера вектора. Затем попробуйте еще раз с вектором, размер которого соответствует ожидаемому количеству элементов. Вы увидите разницу.
Подвеска: производительность имеет такой же большой О. std :: vector выполняет небольшую бухгалтерию, что, по-видимому, требует небольшого количества времени. OTOH, вы в конечном итоге выполняете ту же бухгалтерию при развертывании собственных динамических массивов.
Да, я понимаю. Однако суть его вопроса заключалась в том, каковы различия в производительности ... Я попытался решить эту проблему.
Gcc std :: vector действительно увеличивает емкость один за другим, если вы вызываете push_back.
@bjhend Значит, std::vector от gcc звучит несовместимо со стандартами? Я считаю, что стандарт требует, чтобы vector::push_back амортизировал постоянную сложность, а увеличение емкости на 1 для каждого push_back будет иметь сложность n ^ 2 после учета повторных блоков. - предполагая некоторое экспоненциальное увеличение емкости на push_back и insert, отказ от reserve приведет максимум к постоянному увеличению количества копий векторного контента. Коэффициент роста экспоненциального вектора 1,5 будет означать примерно в 3 раза больше копий, если вы не справитесь с reserve().
@Yakk: согласно стандарту он имеет линейную сложность для каждого вызова вставки, но не для нескольких независимых вставок. Это играет роль только в том случае, если вы вставляете несколько элементов, вызывая std :: vector :: insert. Резервирование большего объема памяти, чем требуется, может вызвать проблемы в системах с ограниченным объемом памяти. Вероятно, поэтому стандарт этого не требует. Фактически, я также ожидал экспоненциального роста с STL GCC, поскольку это не запрещено стандартом. Вот почему я в первую очередь изучил источники std :: vector.
@bjhend ты ошибаешься. Стандарт запрещает экспоненциальный рост: в пункте 16 § 23.2.3 говорится: «В таблице 101 перечислены операции, которые предусмотрены для некоторых типов контейнеров последовательностей, но не для других. Реализация должна обеспечивать эти операции для всех типов контейнеров, указанных в столбце« контейнер », и должен реализовывать их так, чтобы использовать амортизированное постоянное время ». (таблица 101 - это та, в которой есть push_back). А теперь, пожалуйста, прекратите распространять FUD. Ни одна из основных реализаций не нарушает это требование. Стандартная библиотека Microsoft C++ увеличивается в 1,5 раза, а GCC - в 2 раза.
@ R.MartinhoFernandes: Я хотел выяснить именно эти факторы роста, когда заглядывал в источники std :: vector. Я сделал это довольно давно, поэтому не могу вспомнить детали. Когда у меня будет время, я вернусь к текущей версии кода.
Если вы компилируете программное обеспечение в режиме отладки, многие компиляторы не будут встраивать функции доступа вектора. Это сделает реализацию вектора stl намного медленнее в обстоятельствах, когда производительность является проблемой. Это также упростит отладку кода, поскольку вы можете видеть в отладчике, сколько памяти было выделено.
В оптимизированном режиме я ожидал бы, что вектор stl приблизится к эффективности массива. Это связано с тем, что многие векторные методы теперь встроены.
Об этом важно упомянуть. Профилирование отладки STL происходит очень и очень медленно. И это одна из причин, почему люди считают, что STL медленный.
Помнить:
"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%".
(Спасибо метаморфоза за полную цитату)
Не используйте массив C вместо вектора (или чего-то еще) только потому, что вы считаете, что он быстрее, поскольку он должен быть более низким уровнем. Вы ошибаетесь.
Используйте вектор по умолчанию (или безопасный контейнер, адаптированный к вашим потребностям), а затем, если ваш профилировщик говорит, что это проблема, посмотрите, можете ли вы его оптимизировать, используя лучший алгоритм или изменив контейнер.
При этом мы можем вернуться к исходному вопросу.
Классы массивов C++ ведут себя лучше, чем низкоуровневые массивы C, потому что они много знают о себе и могут отвечать на вопросы, которые массивы C. Они умеют убирать за собой. И что еще более важно, они обычно пишутся с использованием шаблонов и / или встраивания, что означает, что то, что кажется большим количеством кода при отладке, разрешается в небольшом количестве кода или его отсутствии в сборке выпуска, что означает отсутствие разницы с их встроенной менее безопасной конкуренцией.
В целом, он делится на две категории:
Использование указателя на массив malloc-ed / new-ed будет в лучшем случае таким же быстрым, как версия std :: vector, и намного менее безопасным (см. сообщение литб).
Так что используйте std :: vector.
Использование статического массива будет в лучшем случае:
Так что используйте std :: array.
Иногда использование vector вместо необработанного буфера влечет за собой видимые затраты, потому что vector инициализирует буфер при построении, в то время как код, который он заменяет, этого не делает, как заметил Берни в его отвечать.
В этом случае вы можете справиться с этим, используя unique_ptr вместо vector или, если случай не является исключительным в вашей кодовой строке, фактически напишите класс buffer_owner, который будет владеть этой памятью и предоставит вам простой и безопасный доступ к его, включая бонусы, такие как изменение его размера (с помощью realloc?), или все, что вам нужно.
Спасибо за обращение к статическим массивам - std :: vector бесполезен, если вам не разрешено динамически выделять память из соображений производительности.
Когда вы говорите: «Использование статического массива будет в лучшем случае так же быстро, как версия boost :: array», это показывает, насколько вы предвзяты. Это должно быть наоборот, Boost: array может быть в лучшем случае быстрым, как статические массивы.
@toto: Это недоразумение: вы должны прочитать это как «Использование статического массива будет в лучшем случае ((так же быстро, как версия boost :: array) && (намного менее безопасно))». Я отредактирую пост, чтобы прояснить это. Кстати, спасибо за сомнения.
Используйте версию Boost (если у вас нет проблем с использованием Boost по какой-то произвольной политической причине) - FTFY
Я пришел из редактора программного обеспечения, где по странным причинам о повышении не могло быть и речи, и пришел в редактор программного обеспечения, в котором о повышении, ну, более или менее, тоже не может быть и речи ...: - / ... Мне повезло парень! Мне просто нужно найти, куда пошла моя удача ... :-)
как насчет std :: array?
Всегда показывать полную цитату. «Программисты тратят огромное количество времени на размышления или беспокойство о скорости некритичных частей своих программ, и эти попытки повышения эффективности на самом деле имеют сильное негативное влияние, когда рассматриваются отладка и обслуживание. Мы должны забыть о небольшой эффективности, например 97% случаев: преждевременная оптимизация - корень всех зол. Тем не менее, мы не должны упускать наши возможности в этих критических 3% ». В противном случае это станет бессмысленным звуковым отрывком.
@metamorphosis, никогда не знал, что это не полная цитата! Спасибо, что поделились!
@metamorphosis: Спасибо за полную цитату. На самом деле я цитировал его вариант! «Преждевременная оптимизация является корнем всех зол, как и преждевременная пессимизация», - говорят Саттер и Александреску (IIRC). Я обновлю свой ответ, чтобы поставить полную цитату.
Ваше сравнение динамического массива с вектором не принимает во внимание начальное распределение std::vector по сравнению с массивом. Подробнее см. Мой ответ: stackoverflow.com/a/43955169/1030527
@bernie: Ваш случай - крайний случай. И это легко решается. Используя стандартную библиотеку: просто запустите свой буфер, поместите его в интеллектуальный указатель (скажем, unique_ptr <char [], FreeDeleter> и покончите с этим. Написание собственного класса: используйте векторный интерфейс, когда это необходимо, и добавьте функция "перераспределения" для хихиканья и веселья. В общем, честно говоря, когда вы кодируете на C++, вы редко используете неинициализированный буфер по множеству очень веских причин. векторно без каких-либо видимых затрат. За оставшийся 1% C++ позволяет вам настроить лучшее решение.
@paercebal Да, это можно рассматривать как крайний случай, но он существует в реальном коде. И да, использование unique_ptr - это способ управлять удалением, но это ортогонально этому вопросу. Неинициализированный буфер может быть полезен, когда он предназначен для назначения memcpy.
@bernie: Я добавил крайний случай в свой ответ. Спасибо.
@paercebal определяет распределитель и перегружает метод 'construct', который пропускает инициализацию при запросе построения по умолчанию для встроенных типов. Затем используйте настраиваемый распределитель как второй параметр шаблона для вектора; край поднялся.
Векторы используют немного больше памяти, чем массивы, поскольку они содержат размер массива. Они также увеличивают размер жесткого диска программ и, возможно, объем памяти программ. Это небольшое увеличение, но может иметь значение, если вы работаете со встроенной системой. Хотя в большинстве мест, где эти различия имеют значение, вы бы использовали C, а не C++.
Если это имеет значение, то очевидно, что вы не используете массивы с динамическим размером, и поэтому ваши массивы не нуждаются в изменении размера. (Если бы они это сделали, вы бы как-то сохранили размер). Следовательно, вы можете использовать boost :: array, если я не ошибаюсь - а почему вы говорите, что для этого нужно где-то «хранить размер»?
О вкладе дули с моими собственными измерениями.
Вывод таков, что массивы целых чисел быстрее, чем векторы целых чисел (в моем примере в 5 раз). Однако массивы и векторы имеют одинаковую скорость для более сложных / не выровненных данных.
Следующий простой тест:
Объяснение теста производительности C++ Array vs Vector
противоречит выводам из «Сравнение ассемблерного кода, созданного для базовых операций индексирования, разыменования и увеличения векторов и массивов / указателей».
Между массивами и векторами должна быть разница. Тест говорит так ... просто попробуйте, код есть ...
У вас еще меньше причин использовать простые массивы в C++ 11.
В природе существует 3 типа массивов, от самых быстрых до самых медленных, в зависимости от имеющихся у них функций (конечно, качество реализации может сделать работу очень быстрой даже для случая 3 в списке):
std::array<T, N>dynarray в C++ TS после C++ 14. В C есть VLAstd::vector<T>Для простых статических массивов 1. с фиксированным числом элементов используйте std::array<T, N> в C++ 11.
Для массивов фиксированного размера 2., указанных во время выполнения, но это не изменит их размер, обсуждается в C++ 14, но он был перемещен в техническую спецификацию и, наконец, сделан из C++ 14.
Для 3.std::vector<T>обычно запрашивает память в куче. Это может иметь последствия для производительности, хотя вы можете использовать std::vector<T, MyAlloc<T>> для улучшения ситуации с помощью специального распределителя. Преимущество по сравнению с T mytype[] = new MyType[n]; заключается в том, что вы можете изменять его размер, и он не будет преобразовываться в указатель, как это делают простые массивы.
Используйте стандартные типы библиотек, упомянутые, чтобы избежать массивы, распадающиеся на указатели. Вы сэкономите время на отладку, а производительность точно такая же, как и с обычными массивами, если вы используете тот же набор функций.
std :: dynarray. После рассмотрения комментариев национальных органов к n3690, этот компонент библиотеки был исключен из рабочего документа C++ 14 в отдельную Техническую спецификацию. Этот контейнер не является частью проекта C++ 14 от n3797. от en.cppreference.com/w/cpp/container/dynarray
очень хороший ответ. краткий и обобщающий, но более подробный, чем какой-либо.
Определенно существует влияние на производительность использования std::vector по сравнению с необработанным массивом, когда вам нужен буфер неинициализированный (например, для использования в качестве места назначения для memcpy()). std::vector инициализирует все свои элементы, используя конструктор по умолчанию. Сырой массив не будет.
спецификация C++ для конструктора std:vector, принимающего аргумент count (это третья форма), утверждает:
`Constructs a new container from a variety of data sources, optionally using a user supplied allocator alloc.
- Constructs the container with count default-inserted instances of T. No copies are made.
Complexity
2-3) Linear in count
Необработанный массив не требует затрат на инициализацию.
Обратите внимание, что с помощью настраиваемого распределителя можно избежать «инициализации» элементов вектора (т.е. использовать инициализацию по умолчанию вместо инициализации значения). См. Эти вопросы для получения более подробной информации:
Но именно здесь мой класс small_vector имеет перегрузку resize, которая по умолчанию создает данные, а не значения, как все обычные методы.
Этот ответ будет лучше, если вы проведете более четкое различие между построением по умолчанию и построением значений. std::vector будет создавать значение всегда, что может иметь небольшие накладные расходы в некоторых крайних случаях. В указанном вами бите конструктора значения вектора конструируются, несмотря на то, что подразумевается, что они строятся по умолчанию, что очень раздражает.
@MooingDuck Я не буду здесь повторять то, что уже подробно объясняется во многих местах. Однако я добавил дополнительную информацию, чтобы показать, что для достижения инициализации по умолчанию можно использовать настраиваемый распределитель.
Предполагая, что массив фиксированной длины (например, int* v = new int[1000]; против std::vector<int> v(1000);, при этом размер v остается фиксированным на уровне 1000), единственное соображение производительности, которое действительно имеет значение (или, по крайней мере, имело значение для меня, когда я был в аналогичной дилемме), - это скорость доступ к элементу. Я просмотрел векторный код STL и нашел вот что:
const_reference
operator[](size_type __n) const
{ return *(this->_M_impl._M_start + __n); }
Эта функция наверняка будет встроена компилятором. Итак, пока единственное, что вы планируете делать с v, - это обращаться к его элементам с помощью operator[], похоже, что на самом деле не должно быть никакой разницы в производительности.
Если вы используете векторы для представления многомерного поведения, это снижает производительность.
Вызывают ли 2d + векторы снижение производительности?
Суть в том, что есть небольшие накладные расходы на каждый субвектор, имеющий информацию о размере, и не обязательно будет сериализация данных (как в случае с многомерными массивами c). Отсутствие сериализации может предложить больше, чем возможности микро-оптимизации. Если вы работаете с многомерными массивами, может быть лучше просто расширить std :: vector и использовать собственную функцию получения / установки / изменения размера бит.
Для массивов фиксированной длины производительность такая же (по сравнению с вектором <>) в сборке выпуска, но в отладочной сборке низкоуровневые массивы выигрывают в 20 раз, по моему опыту (MS Visual Studio 2015, C++ 11).
Таким образом, аргумент «сэкономить время на отладку» в пользу STL может быть действителен, если вы (или ваши коллеги) склонны вносить ошибки в использование массива, но, возможно, нет, если время отладки в основном ожидает, пока ваш код дойдет до точки, в которой в настоящее время работают, чтобы вы могли пройти через него.
Во вторую группу иногда попадают опытные разработчики, работающие над численно насыщенным кодом (особенно если они используют вектор :)).
Почему вы думаете, что существует разрыв в производительности?