vector :: at vs. vector :: operator [] - PullRequest
       14

vector :: at vs. vector :: operator []

72 голосов
/ 21 февраля 2012

Я знаю, что at() медленнее, чем [] из-за проверки границ, что также обсуждается в аналогичных вопросах, таких как C ++ Vector at / [], скорость оператора или :: std:: vector :: at () против оператора [] << удивительные результаты !!В 5-10 раз медленнее / быстрее! </a>.Я просто не понимаю, для чего подходит метод 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;

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

Ответы [ 8 ]

58 голосов
/ 21 февраля 2012

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

16 голосов
/ 21 февраля 2012

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

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

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

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

11 голосов
/ 21 февраля 2012

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

Важным моментом здесь является то, что исключения позволяют отделить нормальный поток кода от логики обработки ошибок, а один блок перехвата может обрабатывать проблемы, возникающие в любом из множества сайтов выброса, даже если они разбросаны глубоко внутри вызовов функций. Таким образом, дело не в том, что 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

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

5 голосов
/ 23 июля 2012

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

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

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

5 голосов
/ 21 февраля 2012

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

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

1 голос
/ 21 февраля 2012

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

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

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

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

0 голосов
/ 28 января 2018

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

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

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

0 голосов
/ 22 февраля 2012

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

...