Когда у вас есть несколько операторов возврата в функции, это называется «ранним возвратом». Если вы выполняете поиск в Google для «досрочного возврата» вы найдете ссылку за ссылкой, в которой говорится, что это плохо.
Я говорю чушь.
Есть две основные причины и одна вторичная причина, по которой люди утверждают, что раннее возвращение - это плохо. Я пройдусь по ним и приведу свое опровержение по порядку. Имейте в виду, что это все мое мнение, и в конечном итоге вы должны решить для себя.
1) Причина: раннее возвращение затрудняет уборку.
Опровержение: для этого нужен RAII. Хорошо спроектированная программа не будет распределять ресурсы таким образом, что, если выполнение рано покинет область видимости, эти ресурсы утекут. Вместо этого:
...
int foo()
{
MyComplexDevice* my_device = new MyComplexDevice;
// ...
if( something_bad_hapened )
return 0;
// ...
delete my_device;
return 42;
}
ты делаешь это:
int foo()
{
std::auto_ptr<MyComplexDevice> my_device(new MyComplexDevice);
if( something_bad_hapened )
return 0;
// ...
return 42;
}
А досрочный возврат не вызовет утечки ресурсов. В большинстве случаев вам даже не нужно использовать auto_ptr, потому что вы будете создавать массивы или строки, в этом случае вы будете использовать vector, string или что-то подобное.
В любом случае вы должны разрабатывать свой код таким образом для устойчивости из-за возможности исключений. Исключения - это форма раннего возврата, такая как явный оператор return, и вы должны быть готовы их обработать. Вы не можете обрабатывать исключение в foo(), но foo() не должно происходить утечки.
2) Причина: ранние возвраты делают код более сложным. Опровержение: ранний возврат действительно упрощает код.
Это общая философия, согласно которой функции должны нести одну ответственность. Я согласен с этим. Но люди заходят слишком далеко и приходят к выводу, что если функция имеет несколько возвратов, у нее должно быть несколько функций. (Они расширяют это, говоря, что функции никогда не должны быть длиннее 50 строк или какого-либо другого произвольного числа.) Я говорю нет. Тот факт, что функция несет только одну ответственность, не означает, что ей нечего делать, чтобы выполнить эту ответственность.
Возьмем, к примеру, открытие базы данных. Это одна ответственность, но она состоит из множества шагов, каждый из которых может пойти не так. Откройте соединение. Авторизоваться. Получить объект подключения и вернуть его. 3 шага, каждый из которых может выйти из строя. Вы можете разбить это на 3 подэтапа, но тогда вместо такого кода:
int foo()
{
DatabaseObject db = OpenDatabase(...);
}
у вас будет:
int foo()
{
Connection conn = Connect(...);
bool login = Login(...);
DBObj db = GetDBObj(conn);
}
Итак, вы действительно только что переместили предполагаемые множественные обязанности в более высокую точку стека вызовов.
3) Причина: множественные точки возврата не объектно-ориентированы. Опровержение: на самом деле это просто еще один способ сказать: «Все говорят, что многократные возвраты - это плохо, хотя я действительно не знаю почему».
Иными словами, это на самом деле просто попытка втиснуть все в коробку в форме объекта, даже если это не место. Конечно, возможно, связь - это объект. Но разве логин? Попытка входа в систему не является (ИМО) объектом. Это операция. Или алгоритм. попытка взять этот алгоритм и втиснуть его в коробку в форме объекта - это беспричинная попытка ООП и приведет только к более сложному, сложному в поддержке и, возможно, еще менее эффективному коду.
person
John Dibling
schedule
21.11.2010