do
{
if () break;
if () break;
} while(false);
switch(0)
{ case 0:
if () break;
}
тут недавно пробегала тема с
for(;;)
{
if () break;
if () continue;
break;
}
и там же мелькал if_break
Собственно вопрос — чем такой код лучше goto?
Я в курсе, что goto стоит избегать и согласен с этим, но зачем его маскировать циклами или switch? Все же goto встроен в С/С++, а идиома "цикл, который выполняется 1 раз" — какая-то искусственная...
10.09.10 12:19: Перенесено модератором из 'C/C++' — Кодт
Здравствуйте, enji, Вы писали:
E>В последнее время часто встречаю что-то вроде E>
E>do
E>{
E> if () break;
E> if () break;
E>} while(false);
E>
E>Собственно вопрос — чем такой код лучше goto?
Он более структурный и предсказуемый (для goto придётся ещё метки ставить — громоздко и можно ошибиться).
Кстати, можно вынести внутренности цикла в отдельный метод с говорящим названием и вместо break использовать return.
Здравствуйте, enji, Вы писали:
E>Собственно вопрос — чем такой код лучше goto?
На правах ИМХО.
goto по своей природе слишком "мощный". Если я вижу break, я точно знаю, что переход произойдёт в конец текущего блока-цикла. Если я вижу goto, то знаю, что перейти он может куда угодно, и восприятие кода сразу усложняется. Кроме того, с goto легче допустить какой-нибудь ляпсус, воткнув пару операций между концом блока и меткой, или просто опечатавшись каким-нибудь CYCLE1_END вместо CYCLE2_END, а потом удивляться, чего это поток управления скачет по всей функции взад-вперёд.
Здравствуйте, andy1618, Вы писали:
A>Кстати, можно вынести внутренности цикла в отдельный метод с говорящим названием и вместо break использовать return.
а из двойного цикла вы как предлагаете выходить, кучу флажков с проверкой вставлять?
Здравствуйте, andy1618, Вы писали: A>Он более структурный и предсказуемый
Насколько я понимаю, "нехороши" все нарушения потока выполнения — break, continue, много return-ов...
Правда goto — самый нехороший
A>(для goto придётся ещё метки ставить — громоздко и можно ошибиться).
Ну а так приходится писать do {} while(false) — еще более громоздко...
A>Кстати, можно вынести внутренности цикла в отдельный метод с говорящим названием и вместо break использовать return.
Понятно, что можно переписать код без goto; часто он при этом улучшается (а иногда — ухудшается ), вопрос в другом — зачем "маскировать" goto?
Здравствуйте, CaptainFlint, Вы писали:
CF>На правах ИМХО. CF>goto по своей природе слишком "мощный". Если я вижу break, я точно знаю, что переход произойдёт в конец текущего блока-цикла. Если я вижу goto, то знаю, что перейти он может куда угодно, и восприятие кода сразу усложняется. Кроме того, с goto легче допустить какой-нибудь ляпсус, воткнув пару операций между концом блока и меткой, или просто опечатавшись каким-нибудь CYCLE1_END вместо CYCLE2_END, а потом удивляться, чего это поток управления скачет по всей функции взад-вперёд.
Это да, но ведь и сам цикл — довольно "сложная" конструкция, затрудняющая понимание программы. Я вижу goto — это именно переход; я вижу цикл — зачем он? Сколько раз мы по нему пройдем, когда выйдем? А если while(false) — далеко внизу и не бросается в глаза...
Я не спорю с тем, что код с большим кол-вом goto сложен для чтения и понимания; скорее всего его стоит переписать. Но маскировать goto циклами — какое-то странное решение, ИМХО
Здравствуйте, Sni4ok, Вы писали:
A>>Кстати, можно вынести внутренности цикла в отдельный метод с говорящим названием и вместо break использовать return.
Для этого нужны вложенные фунцкии, как в паскале. Для одептов GCC и Delphi, для виндоморонов и C++ -- никак нельзя.
S>а из двойного цикла вы как предлагаете выходить, кучу флажков с проверкой вставлять?
Профессиональные программисты используют longjmp()! goto -- для ламиров!
Здравствуйте, enji, Вы писали:
E>Я не спорю с тем, что код с большим кол-вом goto сложен для чтения и понимания; скорее всего его стоит переписать. Но маскировать goto циклами — какое-то странное решение, ИМХО
Это не более, чем плохонькая замена вложенным и анонимным функциям. В нормальных языках оно искаропки есть.
Капитан на вопрос ответил полностью. О себя хочу добавить, что ничего нового или необычного в тех конструкциях нет. Я даже не могу понять, как еще можно было написать алгоритм, который читает и обрабатывает какие-то там данные будь то из сокета или из файла, если не известно сколько их там и надо обрабатывать ошибки. Можно, конечно, вместо тех break-ов поставить условия в операторы циклов или поменять условия наоборот вместо continue, но иногда это не делают по разным причинам. По мне, так особенно большой связи с гото не наблюдается, за исключением того, что все нетривиальные нелинеиные программы в конечном счете набор операций и jump-ов из одного места в другое, как и гото.
Здравствуйте, dilmah, Вы писали:
fk0>> Для этого нужны вложенные фунцкии, как в паскале. Для одептов GCC и Delphi, для виндоморонов и C++ -- никак нельзя.
D>но в С++ есть локальные структуры.
локальные структуры в отличие от вложенных функций (в дельфи\gcc) не могут использовать локальные переменные...
Здравствуйте, enji, Вы писали:
E>локальные структуры в отличие от вложенных функций (в дельфи\gcc) не могут использовать локальные переменные...
Но могут иметь члены-данные, разделённые между всеми вложенными методами. Не самый удобный вариант на многих сценариях, лямбды из C++0x были бы предпочтительнее.
Здравствуйте, enji, Вы писали:
E>Это да, но ведь и сам цикл — довольно "сложная" конструкция, затрудняющая понимание программы. Я вижу goto — это именно переход; я вижу цикл — зачем он? Сколько раз мы по нему пройдем, когда выйдем? А если while(false) — далеко внизу и не бросается в глаза...
Да, есть такая проблема. Но таковы ограничения языка, что у него нет нормальных способов работы с выделенными блоками кода. Поэтому сама идея использования цикла для однократного прохода поначалу выглядит несколько невнятно и может сбивать с толку. Однако после понимания сути происходящего удобство использования подобной конструкции может превысить неудобства синтаксиса.
Кроме того, если не нравится цикл, можно его замаскировать под блок какими-нибудь дефайнами типа BLOCK_BEGIN (do), BLOCK_END (while(0)), BLOCK_EXIT (break). Это скорее вопрос привычки и личного восприятия.
E>Я не спорю с тем, что код с большим кол-вом goto сложен для чтения и понимания; скорее всего его стоит переписать. Но маскировать goto циклами — какое-то странное решение, ИМХО
Про вермишелевый код с кучей goto я сейчас не говорю, это тема отдельного разбирательства. Тут всё-таки речь идёт не о goto как таковых, а о конкретном сравнении двух подходов: а) обычный блок кода с выходом из него по goto (блок тут не в смысле языковой конструкции, а просто с точки зрения логики кода), б) псевдо-цикл с однократным проходом и выходом по break, исполняющему роль того же goto. Так вот, ключевое слово break позволяет читателю кода сразу же понять, куда пойдёт программа в случае выполнения соответствующего условия. А именно, break однозначно утверждает, что точка перехода находится ниже по коду программы и совпадает с точкой окончания текущего цикла. Конечно, это не исчерпывающая информация — до конца цикла тоже ещё дойти надо. Но если вместо break стоит goto, то у нас даже этой неполной информации нет, т.к. метка может располагаться где угодно. Да, её можно назвать END_OF_CYCLE, да, её можно расположить в конце цикла. Но можно ведь и не располагать, вот в чём проблема. И читатель кода, пока явным образом не полезет и не найдёт точное расположение метки, не может быть уверенным, что программист воткнул эту метку куда надо было, а не куда захотелось его левой пятке.