Подтвердить что ты не робот

С++ std::vector против массива в реальном мире

Я новичок в C++. Я читаю "Начало C++ через программирование игр" Майкла Доусона. Тем не менее, я не новичок в программировании в целом. Я только что закончил главу, посвященную векторам, поэтому у меня возник вопрос об их использовании в реальном мире (я студент информатики, поэтому у меня пока нет особого опыта в реальной жизни).

У автора есть Q/A в конце каждой главы, и одна из них была:

В: Когда я должен использовать вектор вместо массива?

A: Почти всегда. Векторы эффективны и гибки. Они требуют немного больше памяти, чем массивы, но этот компромисс почти всегда стоит преимуществ.

Что вы ребята думаете? Я помню, как узнал о векторах в книге по Java, но мы не рассматривали их вообще в моем вступлении к Comp. Sci. класс, ни мой класс Data Structures в колледже. Я также никогда не видел, чтобы они использовались в каких-либо программных заданиях (Java и C). Это заставляет меня чувствовать, что они не очень часто используются, хотя я знаю, что школьный код и реальный код могут сильно отличаться.

Мне не нужно рассказывать о различиях между двумя структурами данных; Я очень осведомлен о них. Все, что я хочу знать, это то, дает ли автор хороший совет в своей Q/A, или он просто пытается уберечь начинающих программистов от разрушения себя сложностями управления структурами данных фиксированного размера. Кроме того, независимо от того, что вы думаете о совете автора, что вы видите в реальном мире чаще?

4b9b3361

Ответ 1

  A: Почти всегда [используйте вектор вместо массива]. Векторы эффективны и гибки. Они требуют немного больше памяти, чем массивы, но этот компромисс почти всегда стоит преимуществ.

Это чрезмерное упрощение. Довольно часто используются массивы, и они могут быть привлекательными, когда:

  • элементы указываются во время компиляции, например, const char project[] = "Super Server";, const Colours colours[] = { Green, Yellow };

    • с C++ 11 будет одинаково кратко инициализировать std::vector со значениями

  • количество элементов изначально фиксировано, например, const char* const bool_to_str[] = { "false", "true" };, Piece chess_board[8][8];

  • производительность при первом использовании имеет решающее значение: при использовании массивов констант компилятор может часто записывать в память исполняемый снимок полностью предварительно инициализированных объектов в исполняемый образ, который затем сбрасывается со страницы непосредственно на место, готовое к использованию, поэтому обычно это происходит гораздо быстрее распределение кучи во время выполнения (new[]) с последующим последовательным построением объектов

    • Сгенерированные компилятором таблицы данных const всегда могут быть безопасно прочитаны несколькими потоками, тогда как данные, созданные во время выполнения, должны завершить построение, прежде чем другой код, запущенный конструкторами для не-локальных переменных static, попытается использовать эти данные: в конечном итоге вам понадобится какой-то способ Singleton (возможно, многопоточный, который будет еще медленнее)

    • В C++ 03 vector, созданный с начальным размером, будет создавать один объект-прототип элемента, а затем копировать каждый элемент данных. Это означало, что даже для типов, в которых конструирование было намеренно оставлено как бездействие, все равно стоило копировать элементы данных - копировать их значения "что угодно, что осталось в памяти". Очевидно, что массив неинициализированных элементов работает быстрее.

  • Одна из мощных функций C++ заключается в том, что часто вы можете написать class (или struct), который точно моделирует схему памяти, требуемую конкретным протоколом, а затем нацеливает указатель класса на память, необходимую для работы. с помощью удобно интерпретировать или назначать значения. Что бы там ни было, многие из таких протоколов часто содержат небольшие массивы фиксированного размера.

  • Существует десятилетний хак для размещения массива из 1 элемента (или даже 0, если ваш компилятор допускает это как расширение) в конце структуры/класса, наведения указателя на тип структуры в некоторой большей области данных и доступа к элементы массива выходят из конца структуры, основываясь на предварительных знаниях о доступности памяти и ее содержимом (при чтении до записи) - см. зачем нужен массив с нулевыми элементами?

  • классы/структуры, содержащие массивы, все еще могут быть типами POD

  • массивы облегчают доступ в разделяемую память от нескольких процессов (по умолчанию внутренние указатели vector на фактические динамически распределяемые данные не будут находиться в разделяемой памяти или не имеют смысла для разных процессов, и было очень трудно заставить C++ 03 vector используйте разделяемую память, как эта, даже при указании пользовательского параметра шаблона распределителя).

  • встраивание массивов может локализовать требования к доступу к памяти, улучшая попадания в кэш и, следовательно, производительность

Тем не менее, если не активная боль использовать vector (в краткости кода, удобочитаемости или производительности), то вам лучше сделать это: они size(), проверили произвольный доступ через at(), итераторы, изменение размера (которое часто становится необходимым по мере того, как приложение "созревает") и т.д. Также часто проще перейти с vector на какой-либо другой стандартный контейнер, если в этом есть необходимость, и безопаснее/проще применять стандартные алгоритмы (x.end() лучше, чем x + sizeof x / sizeof x[0] в любой день).

ОБНОВЛЕНИЕ: C++ 11 представил std::array<>, который позволяет избежать некоторых затрат на vector - внутреннее использование массива фиксированного размера во избежание дополнительного выделения/освобождения кучи - при этом предлагая некоторые преимущества и функции API: http://en.cppreference.com/w/cpp/container/array.

Ответ 2

Одна из лучших причин использования vector в отличие от массива - это идиома RAII. В принципе, для того, чтобы код С++ был безопасным для исключений, любая динамически распределенная память или другие ресурсы должны быть инкапсулированы внутри объектов. Эти объекты должны иметь деструкторы, которые освобождают эти ресурсы.

Когда исключение отправляется без обработки, ТОЛЬКО вещи, которые вызываются gaurenteed, являются деструкторами объектов в стеке. Если вы динамически выделяете память за пределами объекта, а неперехваченное исключение выбрасывается где-то до его удаления, у вас есть утечка памяти.

Это также хороший способ избежать необходимости использовать delete.

Вы также должны проверить std::algorithm, который предоставляет множество общих алгоритмов для vector и других контейнеров STL.

У меня есть несколько раз написанный код с vector, который в ретроспективе, вероятно, был бы лучше с собственным массивом. Но во всех этих случаях либо a Boost::multi_array, либо Blitz::Array были бы лучше, чем любой из них.

Ответ 3

A std::vector - это просто масштабируемый массив. Это не намного больше. Это не то, что вы узнаете в классе Data Structures, потому что это не интеллектуальная структура данных.

В реальном мире я вижу много массивов. Но я также вижу много устаревших кодовых баз, которые используют программирование на языке C с классами С++. Это не означает, что вы должны запрограммировать этот путь.

Ответ 4

Я собираюсь высказывать свое мнение здесь для кодирования больших массивов/векторов, используемых в науке и технике.

Матрицы на основе указателей в этом случае могут быть немного быстрее, особенно для стандартных типов. Но указатели добавляют опасность возможных утечек памяти. Эти утечки памяти могут привести к более длительному циклу отладки. Кроме того, если вы хотите создать динамический массив на основе указателя, вам нужно его вручную закодировать.

С другой стороны, для стандартных типов векторы медленнее. Они также являются динамическими и безопасными для памяти, если вы не храните динамически выделенные указатели в stl-векторе.

В науке и технике выбор зависит от проекта. насколько важна скорость и время отладки? Например, LAAMPS, который является программным обеспечением для моделирования, использует необработанные указатели, которые обрабатываются через их класс управления памятью. Скорость является приоритетом для этого программного обеспечения. Программное обеспечение, которое я создаю, я должен балансировать скорость, с объемом памяти и временем отладки. Я действительно не хочу тратить много времени на отладку, поэтому я использую вектор STL.

Я хотел добавить дополнительную информацию к этому ответу, который я обнаружил из обширного тестирования массивов больших масштабов и большого количества чтения в Интернете. Таким образом, возникает другая проблема с stl-вектором и массивами больших размеров (один миллион +) в распределении памяти для этих массивов. Вектор Stl использует класс std:: allocator для обработки памяти. Этот класс является распределителем памяти на основе пула. При малой загрузке распределение на основе пула чрезвычайно эффективно с точки зрения скорости и использования памяти. По мере того, как размер вектора попадает в миллионы, стратегия на основе пула становится болотом памяти. Это происходит потому, что тенденция пулов состоит в том, чтобы всегда содержать больше пространства, чем в настоящее время используется stl-вектором.

Для широкомасштабных векторов вам лучше писать собственный векторный класс или использовать указатели (необработанную или какую-то систему управления памятью из boost или библиотеки С++). Есть преимущества и недостатки обоих подходов. Выбор действительно зависит от конкретной проблемы, которую вы решаете (слишком много переменных для добавления здесь). Если вам удастся написать свой собственный векторный класс, убедитесь, что вектор позволяет легко очистить память. В настоящее время для вектора Stl вам необходимо использовать операции свопинга, чтобы сделать то, что действительно должно было быть встроено в класс в первую очередь.

Ответ 5

Это редкий случай в реальном мире, где вы имеете дело с фиксированными коллекциями, известного размера. Почти во всех случаях существует определенная степень неизвестности в том, какой размер набора данных вы будете размещать в своей программе. В самом деле, это признак программы good, которая может содержать широкий спектр возможных сценариев.

Возьмите эти (тривиальные) сценарии в качестве примеров:

  • Вы внедрили представление Контроллер для отслеживания комбатантов AI в FPS. Логика игры порождает случайную количество комбатантов в различных зонах каждые пару секунд. Игрок сбивает комбатантов ИИ по курсу известный только во время выполнения.
  • Адвокат обратился к муниципальному Сайт суда в его штате и запрос количества новых случаев DUI которые пришли ночью. Он выбирает фильтр для списка по набору переменных, включая время произошла авария, почтовый индекс и арестовывающий офицер.
  • Операционная система должна сохранить список адресов памяти в использовании различными программами работает на нем. Количество программ и изменения их памяти в непредсказуемым образом.

В любом из этих случаев может быть сделан хороший аргумент, что список переменных размеров (который вмещает динамические вставки и удаления) будет работать лучше, чем простой массив. С основными преимуществами, связанными с уменьшением необходимости выделения/освобождения памяти для фиксированного массива при добавлении или удалении из него элементов.

Ответ 6

Правило большого пальца: если вы не знаете количество элементов заранее или если количество элементов должно быть большим (скажем, более 10), используйте вектор. В противном случае вы также можете использовать массив. Например, я пишу много кода обработки геометрии, и я определяю строку как ARRAY из 2 координат. Линия определяется двумя точками, и она ВСЕГДА будет определяться ровно двумя точками. Использование вектора вместо массива было бы излишним во многих отношениях, а также с точки зрения производительности.

Другое дело: когда я говорю "массив", я действительно делаю массив MEAN: переменная, объявленная с использованием синтаксиса массива, например int evenOddCount[2]; Если вы рассматриваете выбор между вектором и динамически выделенным блоком памяти, например int *evenOddCount = new int[2];, ответ ясен: USE VECTOR!

Ответ 7

Что касается массивов, то простые целые или строковые массивы очень просты в использовании. С другой стороны, для обычных функций, таких как поиск, сортировка, вставка, удаление, вы можете добиться гораздо большей скорости, используя стандартные алгоритмы (встроенные библиотечные функции) на векторах. Специально, если вы используете векторы объектов. Во-вторых, существует огромная разница в том, что векторы могут динамически увеличиваться по мере увеличения количества объектов. Надеюсь, что это поможет.