Heres один прием я услышал о людях, использующих. Я никогда не видел его в дикой природе все же. И это только относится к C, потому что C++ имеет RAII, чтобы сделать это более идиоматически.
void foo()
{
if (!doA())
goto exit;
if (!doB())
goto cleanupA;
if (!doC())
goto cleanupB;
/* everything has succeeded */
return;
cleanupB:
undoB();
cleanupA:
undoA();
exit:
return;
}
@Greg:
, Почему бы не Ваш пример как это:
void foo()
{
if (doA())
{
if (doB())
{
if (!doC())
{
UndoA();
UndoB();
}
}
else
{
UndoA();
}
}
return;
}
Я не использую goto's сам, однако я действительно работал с человеком, как только это будет использовать их в конкретных случаях. Если я помню правильно, его объяснение было вокруг проблем производительности - у него также были определенные правила для как . Всегда в той же функции и маркировке был всегда НИЖЕ оператора перехода.
Моя жалоба на это - то, что Вы теряете обзор блока; любые локальные переменные, объявленные между gotos, остаются в силе, если из цикла когда-либо убегают. (Возможно, Вы принимаете выполнения цикла навсегда; я не думаю, что это - то, что исходный писатель вопроса спрашивал, все же.)
проблемой обзора является больше проблемы с C++, как некоторые объекты могут быть в зависимости от их dtor, называемого в подходящее время.
Для меня, лучшая причина использовать goto во время многоступенчатого процесса инициализации, где это жизненно важно, чтобы все inits были поддержаны из того, если Вы перестали работать, а-ля:
if(!foo_init())
goto bye;
if(!bar_init())
goto foo_bye;
if(!xyzzy_init())
goto bar_bye;
return TRUE;
bar_bye:
bar_terminate();
foo_bye:
foo_terminate();
bye:
return FALSE;
Я видел goto, используемый правильно, но ситуации обычно ужасны. Это только, когда использование goto
само настолько менее хуже, чем оригинал. @Johnathon Голландия, которая poblem, Вы - версия, менее ясно. люди, кажется, боятся локальных переменных:
void foo()
{
bool doAsuccess = doA();
bool doBsuccess = doAsuccess && doB();
bool doCsuccess = doBsuccess && doC();
if (!doCsuccess)
{
if (doBsuccess)
undoB();
if (doAsuccess)
undoA();
}
}
И я предпочитаю циклы как это, но некоторые люди предпочитают while(true)
.
for (;;)
{
//code goes here
}
@fizzer.myopenid.com: Ваш отправленный фрагмент кода эквивалентен следующему:
while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors
break;
}
}
я определенно предпочитаю эту форму.
Даже при том, что я возненавидел этот шаблон со временем, он внушен в программирование COM.
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
HRESULT hr = S_OK;
IfFailGo( pFoo->PerformAction() );
IfFailGo( pFoo->SomeOtherAction() );
Error:
return hr;
}
У меня ничего нет против gotos в целом, но я могу думать о нескольких причинах, почему Вы не хотели бы использовать их для цикла как Вы, упомянул:
Очень распространенный.
do_stuff(thingy) {
lock(thingy);
foo;
if (foo failed) {
status = -EFOO;
goto OUT;
}
bar;
if (bar failed) {
status = -EBAR;
goto OUT;
}
do_stuff_to(thingy);
OUT:
unlock(thingy);
return status;
}
единственный случай я когда-либо использую goto
, для переходящих форвардов, обычно из блоков, и никогда в блоки. Это избегает злоупотребления do{}while(0)
и другие конструкции, которые увеличивают вложение при тихом поддержании читаемого, структурированного кода.
Knuth написал работу "Структурное программирование с Операторами перехода", можно получить его, например, от здесь . Вы найдете много примеров там.
Если устройству Вареного пудинга не нужен goto, то ни если Вы!;)
void dsend(int count) {
int n;
if (!count) return;
n = (count + 7) / 8;
switch (count % 8) {
case 0: do { puts("case 0");
case 7: puts("case 7");
case 6: puts("case 6");
case 5: puts("case 5");
case 4: puts("case 4");
case 3: puts("case 3");
case 2: puts("case 2");
case 1: puts("case 1");
} while (--n > 0);
}
}
код выше из Википедии запись .
Классическая потребность в GOTO в C следующим образом
for ...
for ...
if(breakout_condition)
goto final;
final:
нет никакого простого способа убежать из вложенных циклов без goto.
Вот мой неглупый пример, (от Stevens APITUE) для системных вызовов Unix, которые могут быть прерваны сигналом.
restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;
// handle real errors
}
альтернатива является вырожденным циклом. Эта версия читает как английский язык, "если системный вызов был прерван сигналом, перезапустите его".
Одно хорошее место для использования goto находится в процедуре, которая может прерваться в нескольких точках, каждая из которых требует различных уровней очистки. Gotophobes может всегда заменять gotos структурированным кодом и серией тестов, но я думаю, что это более просто, потому что это устраняет чрезмерное добавление отступа:
if (!openDataFile()) goto quit; if (!getDataFromFile()) goto closeFileAndQuit; if (!allocateSomeResources) goto freeResourcesAndQuit; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!