2013-09-14 5 views
2

Мне часто приходится писать методы, которые возвращают индекс в массив, где есть вероятность того, что индекс не будет найден. Там три основных подхода, которые я использовал:Лучшая практика для определения нулевого индекса

  1. Определение «нулевой индекс» значение как отрицательное число (т.е. если функция возвращает -1 это означает, что нет никакого действительного индекса для возврата
  2. Определите Значение «null index» как максимальное значение для типа индекса (т.е. функция возвращает INT_MAX, если индекс является int).
  3. Передайте указатель на логическое значение, указывающее, является ли значение возвращаемого значения действительным индексом (т. е. передать указатель на логическое значение в значение true, если возвращаемое значение является фактическим индексом в массиве, , в противном случае возвращаемое значение будет ложным)

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

+1

идеальный вариант использования для [boost :: optional] (http://www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/index.html) imho (ps: Я довольно уверен, что в C++ 14 будет 'std :: optional', поэтому этот подход является« будущим доказательством »). – Borgleader

+1

Только # 1 не является необычным из 3-х опций, и поскольку массив никогда не может иметь индекс отрицательного массива в c или C++, кажется, нет причин не использовать его. – goji

+0

Я не думаю, что это должно быть закрыто для того, чтобы быть основанным на мнениях, потому что вопрос: «Есть ли какой-либо консенсус (...) или есть 4-й вариант?». –

ответ

3

В случае сомнений: скопируйте STL. Например, std :: string :: npos, std :: vector :: end() и т. Д. Я предполагаю, что это будет вариант 2 в вашем списке.

Контейнеры, которые работают как контейнеры STL, могут также повторно использовать алгоритмы STL.

+1

Что касается C++, это также ставит под сомнение, должен ли OP использовать массив или контейнер STL. Как указано в FAQ на C++, использование массива в C++ [обычно обескуражено] (http://www.parashift.com/c++-faq/arrays-are-evil.html). – DavidRR

+0

@DavidRR, я предполагал, что этот вопрос означает, что массив является резервным для контейнера специального назначения, а не буквально массивом. Но во втором чтении вы правы, лучшим ответом на этот вопрос является: прекратите использование собственных массивов. –

+0

Поскольку OP включил тег 'C' с его вопросом, нам, возможно, придется предположить, что в его ситуации нельзя полностью избежать массивов. Но в отношении C++, как правило, следует избегать массивов. – DavidRR

1

Так как индекс в массиве обычно является неотрицательным целым числом, возвращая отрицательное число (например, -1), чтобы указать недействительный индекс является приемлемым подходом.

+1

Если вы не выбрасываете половину своих возможных индексов. –

+0

@Adam - Это зависит от варианта использования. Вот [хороший пример] (http://stackoverflow.com/a/8133853/1497596), в котором показано, как можно правильно использовать отрицательный индекс. Тем не менее, я предполагаю, что ситуация, которую описывает ОП, не требует использования отрицательных значений индекса. – DavidRR

+2

@AdamBurry: Это должно быть проблемой только для массивов 'char'. Например, если вы работаете на 32-битной машине, у вас должен быть как минимум 32-разрядный целочисленный тип со знаком, а для любого массива с элементами из двух байтов или более вы выйдете из адресной памяти, прежде чем запускать из доступных индексов. И если у вас есть один массив 'char', занимающий половину вашей доступной памяти, тогда вы заслуживаете того, чтобы заканчивать индексы, чтобы сделать это таким образом, в первую очередь. –

2

# 4. Возврат отрицательного значения ближайшего нижнего индекса, то есть точки вставки. Сначала добавьте 1, чтобы не беспокоиться о -0. Таким образом, любой отрицательный результат «не найден», и вы можете получить от него точку вставки, если вам это нужно в следующей операции.

1

Другим методом, который в некоторых случаях является полезным, является использование 1 .. N в качестве вашего индекса и индекс вашего NULL равным 0, так что вы можете по какой-то причине по умолчанию от 0-го элемента или накапливаться в 0-й элемент , Таким образом, вы не должны проверять, есть ли у вас индекс NULL.

0

Лучший «нулевой индекс» - это тот, который соответствует нулевой модели. Это индекс, который вообще не существует.

Чтобы снять это, вы обычно делаете исключение.

+3

Бросать исключение, когда это то, что вы ожидаете, будет происходить довольно часто, что не кажется оптимальным. –

+2

Исключения обычно используются для исключительных ситуаций. Семантика возвращаемого значения в вопросе OP «не найдена». Я согласен с @Simon; Я не верю, что эта ситуация требует исключения. – DavidRR

+1

Лично просить индекс чего-то, что не существует, не то, что я бы назвал «то, что я ожидаю, произойдет довольно часто», но есть программисты, которые будут спорить иначе. –

1

Неужели никто не помнит errno? Я бы сказал, что это разумное решение. Если функция возвращает определенное значение, например 0 (поскольку речь идет о индексах массива) и errno == ERANGE, неверный индекс возвращаются:

idx =foo.bar(baz); 
if (!idx && errno == ERANGE) { 
    // problem 
} 

Возможно, вам даже не нужно устанавливать значение, так как вы бы в любом случае необходимо проверить errno.

Таким образом, вы полностью используете массив. Другой подход - это вариант этого варианта, который уже является вариацией подхода с булевым значением: используйте переменную класса-локальной ошибки, аналогичную ошибкам ввода-вывода, которые устанавливают badbit, failbit и/или eofbit. Если вы не используете класс, то я бы сказал, errno/Boolean подход является лучшим.

1

В большинстве случаев, я бы сказал, # 4:

  • Возврат код состояния, указывающий, удалась ли операция или неудачи и возвращает индекс по ссылке (либо с помощью ссылки или указателя в зависимости от версии C).

Преимущества # 4 над # 3 являются:

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

  2. Если ваши функции C всегда возвращают коды состояния повсюду в вашей программе, вы всегда знаете, что код возврата является статусом.

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

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

Ото, если вы программа очень мала, и вы не возражаете, несколько магических констант, то либо # 1 или # 2 морально эквивалентны и могут сделать меньше, чем набрав # 3 или # 4. # 2 имеет несколько преимуществ по сравнению с # 1:

  1. Как уже упоминалось в других ответах, подписанная INT возвращаемое значение может представлять только половину чисел без знака INT может представлять.

  2. Он избегает проблем с сопоставлением с сопоставлением без знака, если вы сравниваете с sizeof (array), std :: vector :: string, которые являются size_t. Чаще всего проблема заключается в том, что предупреждение о компиляторе игнорируется (что приводит к тому, что люди обычно игнорируют предупреждения, когда они этого не делают), или кто-то исправляет это с помощью броска, надеюсь, после анализа, чтобы убедиться, что актер действительно действителен.

Я лично использовал # 1, # 2 и # 4 и нашел шкалы # 4 лучшим. Я предпочитаю по умолчанию использовать # 4, особенно в коде, где сбои распространены и их нужно обрабатывать. Либо 1, либо 2 обычно лучше всего работают, если код меньше, и вы вызываете множество подпрограмм, которые возвращают одну вещь и не могут потерпеть неудачу (например, процедуры обработки изображений).

Смежные вопросы