Ответ 1
Когда у вас есть несколько операторов возврата в функции, это называется "ранним возвратом". Если вы выполните поиск Google для "раннего возвращения", вы найдете ссылку после ссылки, в которой говорится, что это плохо.
Я говорю глупости.
Есть две основные причины и одна вторичная причина, по которой люди утверждают, что раннее возвращение плохое. Я пройду через них и буду приводить в порядок свои опровержения. Имейте в виду, что это все мое мнение, и в конечном итоге вы должны сами решить.
1) Причина: ранние возвращения затрудняют очистку.
Rebuttal: То, что для 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
, потому что вы будете создавать массивы или строки, в chich случае вы будете использовать vector
, string
или что-то подобное.
Вы должны разработать свой код, как это, в любом случае, для надежности, из-за возможности исключений. Исключения представляют собой форму раннего возврата, как явный оператор return
, и вы должны быть готовы к их обработке. Вы не можете обрабатывать исключение в foo()
, но foo()
не должно протекать независимо.
2) Причина: ранние возвращения делают код более сложным. Rebuttal: Ранние возвращения на самом деле упрощают код.
Это общая философия, согласно которой функции должны нести одну ответственность. Я согласен с этим. Но люди делают это слишком далеко и делают вывод, что если функция имеет несколько возвратов, она должна иметь более чем одну ответственность. (Они расширяют это, говоря, что функции не должны быть длиннее 50 строк или какое-то другое произвольное число.) Я говорю "нет". Просто потому, что функция несет только одну ответственность, не означает, что ей нечего делать, чтобы выполнить эту возможность.
Возьмите, например, открытие базы данных. Эта одна ответственность, но она состоит из многих шагов, каждая из которых может пойти не так. Откройте соединение. Авторизоваться. Получите объект соединения и верните его. 3 шага, каждый из которых может потерпеть неудачу. Вы можете разбить это на 3 подэтапа, но вместо этого нужно иметь такой код:
int foo()
{
DatabaseObject db = OpenDatabase(...);
}
у вас получится:
int foo()
{
Connection conn = Connect(...);
bool login = Login(...);
DBObj db = GetDBObj(conn);
}
Итак, вы действительно переместили предполагаемые множественные обязанности на более высокую точку в стеке вызовов.
3) Причина. Несколько точек возврата не являются объектно-ориентированными. Rebuttal: Это действительно просто еще один способ сказать: "Все говорят, что многократные возвращения плохие, хотя я действительно не знаю почему".
Взятый другим способом, это на самом деле просто попытка перерезать все в объектную коробку, даже если она там не принадлежит. Конечно, возможно, соединение является объектом. Но это логин? Ошибка входа в систему (IMO) не является объектом. Это операция. Или алгоритм. пытаясь взять этот алгоритм и вставить его в объектную коробку, - это бесплатная попытка ООП и приведет только к более сложному, сложному и более эффективному коду.