вектор::ПО и вектор::оператор[]


Я знаю, что at() медленнее, чем [] из-за его проверки границ, которая также обсуждается в подобных вопросах, таких как вектор C++ на скорости оператора/ [] или :: std:: vector:: at() vs operator [] . Я просто не понимаю, что такое at() метод хорош для.

если у меня есть простой вектор вроде этого: std::vector<int> v(10); и я решил получить доступ к его элементам с помощью at() вместо [] в ситуации, когда у меня есть индекс i и я не уверен, что его в векторных границах, это заставляет меня оберните его с помощью блока try-catch:

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

хотя я могу сделать то же самое поведение с помощью size() и проверка индекса самостоятельно, что кажется мне проще и гораздо удобнее:

if (i < v.size())
    v[i] = 2;

Итак, мой вопрос:
каковы преимущества использования вектор:: at за вектор::оператор[] ?
когда я должен использовать вектор:: at, а не вектор::размер + вектор::оператор[] ?

8 61

8 ответов:

Я бы сказал исключения, что vector::at() броски на самом деле не предназначены для того, чтобы быть пойманным непосредственно окружающим кодом. Они в основном полезны для отлова ошибок в коде. Если вам нужно проверить границы во время выполнения, потому что, например, индекс поступает из пользовательского ввода, вам действительно лучше всего использовать if заявление. Итак, вкратце, создайте свой код с намерением, что vector::at() никогда не будет выдавать исключение, так что если это произойдет, и ваша программа прерывается, это признак ошибки. (просто как assert())

это заставляет меня обернуть его с try-catch block

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

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

поскольку маловероятно, что доступ за пределы вектора является частью нормального потока программы (в этом случае вы правы: проверьте заранее с size вместо того, чтобы позволить исключению всплывать), я согласен с вашей диагностикой:at в сущности, бесполезно.

каковы преимущества использования vector::at над vector:: operator[] ? Когда я должен использовать vector::at, а не vector::size + vector:: operator[] ?

важным моментом здесь является то, что исключения позволяют отделить нормальный поток кода от логики обработки ошибок, и один блок catch может обрабатывать проблемы, созданные из любого из множества сайтов броска, даже если они разбросаны глубоко внутри вызовов функций. Значит, дело не в этом at() обязательно легче однократное использование, но иногда это становится проще - и меньше запутывания обычной логики-когда у вас есть много индексирования для проверки.

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

в качестве реального примера у меня есть код, который маркирует C++ в лексические элементы, а затем другой код, который перемещает индекс над вектором токенов. В зависимости от того, что встречается, я могу увеличить и проверить следующий элемент, как в:

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

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

во-первых, является ли at() или operator[] медленнее, не уточняется. Когда там нет границ ошибки, я бы ожидал, что они будут примерно с той же скоростью, на по крайней мере, в отладочных сборках. Разница в том, что at() указывает именно то, что произойдет, есть ошибка границ (исключение), где как в случае operator[], это неопределенное поведение-это сбой во всех системах, которые я использую (g++ и VC++), по крайней мере, когда используются обычные флаги отладки. (Еще одно отличие заключается в том, что как только я уверен мой код, я могу получить существенное увеличение скорости operator[] при повороте отладки выключен. Если это требуется-я не сделал бы этого, если бы не было необходимости.)

на практике at() редко подходит. Если контекст таков, что вы знаете, что индекс может быть недействительным, вы, вероятно, хотите явный тест (например, чтобы вернуть значение по умолчанию или что-то), и если вы знаете, что это не может быть недействительным, вы хотите прервать (и если вы не знаете, является ли это может быть недействительным или нет, я бы предложите указать функцию точнее интерфейс). Есть несколько исключений, однако, где недопустимый индекс может быть результатом анализа пользовательских данных и ошибки должен вызвать прерывание всего запроса (но не привести сервер вниз); в таких случаях уместно исключение, и at() будет делать это для тебя.

at может быть понятнее, если у вас есть указатель на вектор:

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

производительность в сторону, первый из них является более простой и понятный код.

весь смысл использования исключений в том, что ваш код обработки ошибок может быть дальше.

в этом конкретном случае пользовательский ввод действительно является хорошим примером. Представьте, что вы хотите семантически проанализировать XML-структуру данных, которая использует индексы для ссылки на какой-то ресурс, который вы внутренне храните в std::vector. Теперь XML-дерево-это дерево, поэтому вы, вероятно, захотите использовать рекурсию для его анализа. Глубоко внутри, в рекурсии, может быть нарушение прав доступа автором XML-файл. В этом случае вы обычно хотите выйти из всех уровней рекурсии и просто отклонить весь файл (или любую "более грубую" структуру). Это где приходит в сподручное. Вы можете просто написать код анализа как-если файл был действителен. Код библиотеки позаботится об обнаружении ошибок, и вы можете просто поймать ошибку на грубом уровне.

кроме того, другие контейнеры, такие как std::map, также std::map::at который имеет несколько иную семантику, чем std::map::operator[]: может быть используется на карте const, в то время как operator[] не может. Теперь, если вы хотите написать контейнерный агностический код, например, что-то, что может иметь дело с любым const std::vector<T>& или const std::map<std::size_t, T>&,ContainerType::at было бы ваше оружие выбора.

тем не менее, все эти случаи обычно появляются при обработке какого-то непроверенного ввода данных. Если вы уверены в своем допустимом диапазоне, как обычно, вы можете использовать operator[], но еще лучше, итераторы с begin() и end().

По данным этой статья, производительность в сторону, это не имеет никакого значения, чтобы использовать at или operator[], только если доступ будет в пределах размера вектора. В противном случае, если доступ основан только на емкости вектора, безопаснее использовать at.

Примечание: похоже, некоторые новые люди понижают этот ответ, не имея любезности сказать, что не так. Ниже Ответ правильный и может быть проверен здесь.

на самом деле есть только одно отличие: at делает проверку границ в то время как operator[] нет. Это касается отладки, а также версии и это очень хорошо, заданного стандартами. Все так просто.

это делает at более медленный метод, но это очень плохой совет, чтобы не использовать at. Вы должны смотреть на абсолютные числа, а не на относительные. Я могу смело поспорить, что большая часть вашего кода делает жирные более дорогие операции, чем at. Лично я стараюсь использовать at потому что я не хочу неприятную ошибку, чтобы создать неопределенное поведение и проникнуть в производство.