множественные операторы возврата или "конец"; - PullRequest
1 голос
/ 20 мая 2011

Что лучше по стилю / удобочитаемости?

У меня есть цикл, который считывает ввод и выполняет разные действия в соответствии с вводом, и когда возникает ошибка, мне нужно просто return;.Пример:

while( get_input() )
{
    if( input == "somethingcool" )
    {
        if( !process_somethingcool() )
            return; // <-- a couple of these
    }
    //...
    else // bad input, error handling is fancier than this, but irrelevant to the question
        return;
}
return;

Так я должен заменить отдельные return; на goto end; и поместить метку end: прямо над последним возвратом в примере выше или нет?Мне не нужно "использовать RAII", потому что в блоках if ничего не выделено.Оба способа были бы идентичны во всех смыслах этого слова, за исключением стиля / читаемости / производительности?

Я бы предположил, что производительность идентична, но просто чтобы быть уверенным: так ли это?

Ответы [ 12 ]

4 голосов
/ 20 мая 2011

Для C goto является разумным (он широко используется в ядре Linux), поскольку вы можете улучшить читабельность с помощью одной точки возврата.у вас неявно есть несколько точек возврата, поэтому вы всегда должны использовать модель RAII с несколькими возвратами.

3 голосов
/ 20 мая 2011

Вы пытаетесь начать религиозную войну?

Серьезно, иногда есть места, где goto - лучший выбор. За двадцать лет я видел около 3 или 4. Многократные возвраты не обязательно являются злом, но если вам придется повторять много кода очистки, они становятся довольно грязными.

Очень часто вы можете изменить код, чтобы сделать такой выбор ненужным. Трудно сделать конкретные предложения, не видя ваш код, но может быть что-то вроде этого:

void f()
{
   bool bDone=false;
   while (!bDone && get_input())
   {
      if (input == "cool")
      {
         process_cool();
         bDone = true;
      }
      else if (input == "unfinished")
      {
         process_something();
      }
      else
      {
          // error
          bDone = true;
      }
   }
}

Большая помощь в рефакторинге - убедиться, что в цикле нет десятков строк. Если у вас есть куча работы, разбейте ее на функции и вызовите небольшое количество функций из цикла while.

Помните, что одна функция должна делать только одно.

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

Если вы все еще в замешательстве, рассмотрите возможность размещения более реалистичного фрагмента кода, и мы сможем подсказать, как лучше всего справляться.

Удачи!

3 голосов
/ 20 мая 2011

Лично мне нравится, чтобы содержимое любых блоков было как можно меньше. Одна строка с вызовом другой функции идеальна (после чистого кода Боба Мартина).

Я бы выбрал вариант, который вы не предложили:

while(get_input()) {
    if(!process_input(input)) {
         break;
    }
}

Где process_input выбирает соответствующую функцию process_..., возвращая все, что возвращает, или false, если ввод неправильный.

2 голосов
/ 20 мая 2011

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

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

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

1 голос
/ 20 мая 2011

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

1 голос
/ 20 мая 2011

return обычно предпочтительнее goto; обычно хулители меток не могут выдвинуть какой-либо фактический аргумент в поддержку своей догмы. 1

Тем не менее, ваш случай более ясен: почему вы выбрали goto? Оба решения эквивалентны, за исключением того, что goto требует, чтобы вы написали end: непосредственно перед концом функции, занимая пространство и выглядя безобразно без причины.

Поэтому из двух представленных вариантов я бы порекомендовал return s. Тем не менее, я бы также рекомендовал серьезно рассмотреть другие предложения, приведенные в ответах на этот вопрос (исключения и ограничение «злобности» ваших условных выражений).


1 Или, если они это делают, это обычно что-то вроде "goto оставляет ваши объекты висящими", что не соответствует действительности (согласно 6.6 / 2 в C +) + 0x FDIS ). goto не ломает модель RAII .

1 голос
/ 20 мая 2011

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

Это сказало: goto не должно использоваться для этого; return гораздо лучший вариант. Если вам нужно что-то сделать в конце вашей программы, независимо от того, почему она выпала, вы должны выбросить исключения и использовать блок catch.

0 голосов
/ 20 мая 2011

Ни.Вы должны реорганизовать код в нечто читаемое и поддерживаемое.В каждом случае, который я видел (и я видел много), где программисту требовалось goto, и в большинстве случаев, когда ему требовалось многократное возвращение (и во всех случаях, когда возвращение было вложено в цикл),лучше решить путем рефакторинга кода в отдельные, более простые функции, каждая из которых имеет один возврат в конце функции.(Многократные возвраты иногда оправданы для очень простых функций; например, я буду использовать их, если единственным оператором в функции является switch, а каждый случай заканчивается return.)

0 голосов
/ 20 мая 2011

Почему бы не структурировать ваш код, используя «охранные предложения» (путем изменения логики) вместо вложенных ifs.Тогда весь вопрос использования goto становится спорным.

while( get_input() )
{
    if( input != "somethingcool" )
    {
        return; //handle error
    }
    if( !process_somethingcool() )
        return; //handle error
    }
}
return; //success
0 голосов
/ 20 мая 2011

На самом деле не рекомендуется использовать goto.Это порождает проблемы с читабельностью кода.Лучше использовать оператор break;, в котором вы хотите выйти из цикла и вернуться в конец кода.Это стандартная процедура.

while( get_input() )
{
    if( input == "somethingcool" )
    {
        if( !process_somethingcool() )
            break; // <-- a couple of these
    }
    //...
    else // bad input, error handling is fancier than this, but irrelevant to the question
        break; //although its better if you handle it somehow here.
}
return;
...