Использовать Гото или нет?


В настоящее время я работаю над проектом, в котором активно используются операторы goto. Основная цель операторов goto-иметь один раздел очистки в подпрограмме, а не несколько операторов возврата. Как показано ниже:

BOOL foo()
{
   BOOL bRetVal = FALSE;
   int *p = NULL;

   p = new int;
   if (p == NULL)
   {
     cout<<" OOM n";
     goto Exit;
   }

   // Lot of code...

Exit:
   if(p)
   {
     delete p;
     p = NULL;
   }
   return bRetVal;
}

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

тем не менее, я читал много мест, это плохая практика, чтобы иметь goto заявления.

В настоящее время я читаю Код книга, и в ней говорится, что нам нужно использовать переменные, близкие к их объявлениям. Если мы используем goto, то нам нужно объявить / инициализировать все переменные перед первым использованием goto в противном случае компилятор выдаст ошибки, что инициализация переменной xx пропускается оператором goto.

какой путь правильный?


из комментария Скотта:

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

но если мы используем goto только для перехода вперед и к одной метке, то это должно быть хорошо(?).

30 56

30 ответов:

Я не уверен, что вы подразумеваете под очисткой кода, но в C++ есть концепция под названием"получение ресурса есть инициализация

(обратите внимание, что в C# и Java это обычно решается с помощью try / finally)

для получения дополнительной информации проверить эту страницу: http://www.research.att.com/~bs / bs_faq2. html#finally

EDIT: позвольте мне прояснить это немного.

рассмотрим следующий код:

void MyMethod()
{
    MyClass *myInstance = new MyClass("myParameter");
    /* Your code here */
    delete myInstance;
}

проблема: что произойдет, если у вас есть несколько выходов из функции? Вы должны отслеживать каждый выход и удалять свои объекты на всех возможных выходах! В противном случае у вас будут утечки памяти и ресурсы зомби, верно?

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

void MyMethod()
{
    MyClass myInstance("myParameter");
    /* Your code here */
    /* You don't need delete - myInstance will be destructed and deleted
     * automatically on function exit */
}

Ах, да, и использовать std::unique_ptr или что-то подобное, потому что пример выше, как это очевидно несовершенно.

Мне никогда не приходилось использовать goto в C++. Когда-либо. КОГДА-ЛИБО. Если есть ситуация, которую следует использовать, это невероятно редко. Если вы действительно рассматриваете возможность сделать goto стандартной частью своей логики, что-то слетело с рельсов.

есть в основном два момента, которые люди делают в отношении gotos и вашего кода:

  1. Гото-это плохо. очень редко можно встретить место, где вам нужны gotos, но я бы не предложил ударить его полностью. Хотя C++ имеет достаточно умный поток управления, чтобы сделать goto редко подходящим.

  2. ваш механизм очистки неверен: этот момент гораздо важнее. В C, используя управление памятью на ваш собственный не только хорошо, но часто лучший способ сделать вещи. В C++ ваша цель должна заключаться в том, чтобы как можно больше избегать управления памятью. Вы должны избегать управления памятью как можно больше. Пусть компилятор сделает это за вас. Вместо того, чтобы использовать new, просто объявлять переменные. Единственный раз, когда вам действительно понадобится управление памятью, - это когда вы заранее не знаете размер своих данных. Даже тогда, вы должны попытаться просто использовать некоторые из STL сборники вместо.

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

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

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

ваш код крайне не-идиоматические и вы никогда не должны писать. Вы в основном эмулируете C в C++ там. Но другие заметили это и указали на Райи как на альтернативу.

код не работает как вы и ожидали, потому что это:
p = new int;
if(p==NULL) { … }

не когда-нибудь оценка для true (кроме тех случаев, когда вы перегружены operator new как ни странно). Если operator new не удается выделить достаточно памяти, он выдает исключение, это никогда,когда-нибудь возвращает 0, по крайней мере, не с этим набором параметров; есть специальное размещение-новая перегрузка, которая принимает экземпляр типа std::nothrow и это действительно возвращает 0 вместо того, чтобы бросать исключение. Но эта версия редко используется в обычном коде. Некоторые низкоуровневые коды или встроенные приложения устройств могут извлечь из этого выгоду в контекстах, где работа с исключениями слишком дорога.

что-то подобное верно для вашего delete блок, как сказал Харальд:if (p) не нужно перед delete p.

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

bool foo() // prefer native types to BOOL, if possible
{
    bool ret = false;
    int i;
    // Lots of code.
    return ret;
}

В общем, и на поверхности, нет ничего плохого в вашем подходе, при условии, что у вас есть только одна метка, и что gotos всегда идут вперед. Например, этот код:

int foo()
{
    int *pWhatEver = ...;
    if (something(pWhatEver))
    { 
        delete pWhatEver;
        return 1;
    }
    else
    {
        delete pWhatEver;
        return 5;
    }
}

и этот код:

int foo()
{
    int ret;
    int *pWhatEver = ...;
    if (something(pWhatEver))
    { 
        ret = 1;
        goto exit;
    }
    else
    {
        ret = 1;
        goto exit;
    }
exit:
    delete pWhatEver;
    return ret;
}

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

однако, во многих случаях RAII (приобретение ресурса инициализация) картина может сделать код намного чище и более ремонтопригоден. Например, этот код:

int foo()
{
    Auto<int> pWhatEver = ...;

    if (something(pWhatEver))
    {
        return 1;
    }
    else
    {
        return 5;
    }
}

короче, легче читать и легче поддерживать, чем оба предыдущих примера.

Итак, я бы рекомендовал использовать подход RAII, если вы можете.

ваш пример не исключение безопасности.

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

также в этот момент (когда bad_alloc выбрасывается), ваш стек будет размотан, пропуская весь код очистки в каждой функции на пути вверх по стеку вызовов таким образом, не очищая ваш код.

нужно смотреть, чтобы сделать некоторые исследования в смарт-указатели. В ситуации выше, вы могли бы просто использовать std::auto_ptr<>.

также обратите внимание, что в коде C++ нет необходимости проверять, является ли указатель нулевым (обычно потому, что у вас никогда не было необработанных указателей), а потому, что new не вернет NULL (он бросает).

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

Я думаю, что другие ответы (и их комментарии) охватили все важные моменты, но вот одна вещь, которая еще не была сделана должным образом:

как должен выглядеть ваш код:

bool foo() //lowercase bool is a built-in C++ type. Use it if you're writing C++.
{
  try {
    std::unique_ptr<int> p(new int);
    // lots of code, and just return true or false directly when you're done
  }
  catch (std::bad_alloc){ // new throws an exception on OOM, it doesn't return NULL
    cout<<" OOM \n";
    return false;
  }
}

Ну, это короче, и, насколько я вижу, более правильно (правильно обрабатывает случай OOM), и самое главное, мне не нужно было писать код очистки или делать что-то особенное, чтобы "убедиться, что мое возвращаемое значение инициализировано".

одна проблема с ваш код, который я действительно заметил, когда писал это: "что, черт возьми, ценность бретваля на данный момент?". Я не знаю, потому что, это было объявлено waaaaay выше, и это было в последний раз назначено когда? В какой-то момент выше этого. Я должен прочитать всю функцию, чтобы убедиться, что я понимаю, что будет возвращено.

и как мне убедить себя, что память освобождается?

Как знаю что мы никогда не забываем, чтобы перейти к метке очистки? Мне пришлось работайте в обратном направлении от метки очистки, находя Гото, что указывает на это, и что более важно, найти те, которые там нет. Мне нужно проследить через все пути функции, чтобы убедиться, что функция очищается должным образом. Это читается как код спагетти для меня.

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

как используется в ядре Linux, goto используется для очистки работы хорошо, когда одна функция должна выполнить 2 или более шагов, которые могут потребоваться отменить. Шаги не должны быть выделением памяти. Это может быть изменение конфигурации на фрагмент кода или в регистре набора микросхем ввода-вывода. Гото должны быть необходимы только в небольшом количестве случаев, но часто при правильном использовании, они могут быть лучшие решение. Они не злые. Они-инструмент.

вместо из...

do_step1;
if (failed)
{
  undo_step1;
  return failure;
}

do_step2;
if (failed)
{
  undo_step2;
  undo_step1;
  return failure;
}

do_step3;
if (failed)
{
  undo_step3;
  undo_step2;
  undo_step1;
  return failure;
}

return success;

вы можете сделать то же самое с goto заявления, как это:

do_step1;
if (failed) goto unwind_step1;

do_step2;
if (failed) goto unwind_step2;

do_step3;
if (failed) goto unwind_step3;

return success;

unwind_step3:
  undo_step3;

unwind_step2:
  undo_step2;

unwind_step1:
  undo_step1;

return failure;

должно быть ясно, что, учитывая эти два примера, один предпочтительнее другого. Что касается толпы RAII... Нет ничего плохого в таком подходе, пока они могут гарантировать, что размотка всегда будет происходить в совершенно обратном порядке: 3, 2, 1. И, наконец, некоторые люди не используют исключения в своем коде и поручают компиляторам отключить их. Таким образом, не весь код должен быть исключение безопасно.

вы должны прочитать эту сводку потоков из списков рассылки ядра Linux (обращая особое внимание на ответы от Линуса Торвальдса), прежде чем формировать политику для goto:

http://kerneltrap.org/node/553/2131

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

for (int i=0; i<10; i++) {
    for (int j=0; j<10; j++)
    {
        if (somecondition==true)
        {
            goto finish;
        }
        //Some code
    }
    //Some code
}
finish:

единственная ситуация, которую я знаю о том, где goto все еще используется в значительной степени, - это язык сборки мейнфреймов, и программисты, которых я знаю, обязательно документируют, где код прыгает и почему.

В общем, вы должны разработать свои программы, чтобы ограничить потребность в gotos. Используйте методы OO для" очистки " возвращаемых значений. Есть способы сделать это, которые не требуют использования gotos или усложнения кода. Есть случаи, когда goto очень полезны (например, глубоко вложенные области), но по возможности следует избегать.

обратная сторона Гото довольно хорошо обсуждается. Я бы просто добавил, что 1) Иногда вы должны использовать их и должны знать, как минимизировать проблемы, и 2) некоторые принятые методы программирования являются goto-in-маскировкой, поэтому будьте осторожны.

1) Когда вам нужно использовать GOTO, например, в ASM или in .bat-файлов, думаю, как компилятор. Если вы хотите код

 if (some_test){
  ... the body ...
}

делать то, что компилятор делает. Создайте метку, цель которой-пропустить тело, а не делать что-либо следует. то есть

 if (not some_test) GOTO label_at_end_of_body
  ... the body ...
label_at_end_of_body:

не

 if (not some_test) GOTO the_label_named_for_whatever_gets_done_next
  ... the body ...

the_label_named_for_whatever_gets_done_next:

другими словами, цель метки не в do, но и пропустить что-то.

2) то, что я называю goto-in-disguise, - это все, что можно превратить в код GOTO+LABELS, просто определив пару макросов. Примером является метод реализации конечных автоматов с помощью переменной состояния и оператора while-switch.

 while (not_done){
    switch(state){
        case S1:
            ... do stuff 1 ...
            state = S2;
            break;
        case S2:
            ... do stuff 2 ...
            state = S1;
            break;
        .........
    }
}

может обернуться в:

 while (not_done){
    switch(state){
        LABEL(S1):
            ... do stuff 1 ...
            GOTO(S2);
        LABEL(S2):
            ... do stuff 2 ...
            GOTO(S1);
        .........
    }
}

всего определив несколько макросов. Почти любой FSA может быть превращен в структурированный код без Гото. Я предпочитаю держаться подальше от кода GOTO-in-disguise, потому что он может попасть в те же проблемы со спагетти-кодом, что и undisguised gotos.

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

Гото обеспечивает лучшую не повторяйся (сухой), когда "хвостовая логика" является общей для некоторых, но не для всех случаев. Особенно в заявлении "switch" я часто использую goto, когда некоторые из ветвей коммутатора имеют хвостовую общность.

switch(){
   case a:  ... goto L_abTail;
   case b: ... goto L_abTail;
L_abTail: <commmon stuff>
    break://end of case b
case c:
.....
}//switch

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

...
   goto L_skipMiddle;
{
    int declInMiddleVar = 0;
    ....
}
L_skipMiddle: ;

С более поздними версиями Visual Studio обнаруживая использование неинициализированных переменных, я всегда инициализирую большинство переменных, хотя я думаю, что они могут быть назначены во всех ветвях - легко закодировать оператор "трассировки", который ссылается на переменную, которая никогда не была назначена, потому что ваш ум не думает о операторе трассировки как о "реальном коде", но, конечно же, Visual Studio все равно обнаружит ошибка.

конечно, если вы на самом деле выделяете ресурсы, то если auto-ptr не подходит, вы действительно должны использовать try-catch, но tail-end-merge-don't-repeat-yourself происходит довольно часто, когда exception-safety не является вопрос.

использование goto для перехода в раздел очистки вызовет множество проблем.

во-первых, разделы очистки склонны к проблемам. Они имеют низкую когезию (нет реальной роли, которую можно описать с точки зрения того, что пытается сделать программа), высокую связь (корректность очень сильно зависит от других разделов кода) и вовсе не являются исключением. Смотрите, если вы можете использовать деструкторы для очистки. Например, если int *p изменено на auto_ptr<int> p, что P указывает на будет автоматически освобождается.

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

Так как это классическая тема, я отвечу с Dijkstra в перейти к заявлению считается вредным (первоначально опубликовано в ACM).

вся цель идиомы every-function-has-a-single-exit-point в C заключалась в том, чтобы поместить все материалы очистки в одно место. Если вы используете деструкторы C++ для обработки очистки, это больше не нужно-очистка будет выполнена независимо от того, сколько точек выхода имеет функция. Таким образом, в правильно разработанном коде C++ больше нет необходимости в таких вещах.

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

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

// Setup
if(
     methodA() &&
     methodB() &&
     methodC()
 )
 // Cleanup

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

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

код, который Вы нам даете, это (почти) код C, написанный внутри файла C++. Тип очистки памяти, который вы используете, будет в порядке в программе C, не использующей код/библиотеки C++.

В C++ ваш код просто небезопасен и ненадежен. В C++ тип управления, который вы просите, делается по-другому. Используйте конструкторы / деструкторы. Используйте умные указатели. Используйте стек. Одним словом, используйте RAII.

ваш код может (т. е. в C++, должен) быть написан как:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   std::auto_ptr<int> p = new int;

   // Lot of code...

   return bRetVal ;
}

(обратите внимание, что создание int несколько глупо в реальном коде, но вы можете заменить int любым видом объекта, и тогда это имеет больше смысла). Давайте представим, что у нас есть объект типа T (T может быть int, некоторый класс C++ и т. д.). Тогда код становится:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   std::auto_ptr<T> p = new T;

   // Lot of code...

   return bRetVal ;
}

или даже лучше, используя стек:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   T p ;

   // Lot of code...

   return bRetVal;
}

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

RAII имеет много граней (т. е. использование интеллектуальных указателей, стека, использование векторов вместо массивов переменной длины и т. д.), но в целом речь идет о написании как можно меньше кода, позволяя компилятору очистить материал в нужный момент.

единственные две причины, по которым я использую goto в своем коде на C++:

  • нарушение уровня 2+ вложенные циклы
  • сложные потоки, как этот (комментарий в моей программе):

    /* Analysis algorithm:
    
      1.  if classData [exporter] [classDef with name 'className'] exists, return it,
          else
      2.    if project/target_codename/temp/classmeta/className.xml exist, parse it and go back to 1 as it will succeed.
      3.    if that file don't exists, generate it via haxe -xml, and go back to 1 as it will succeed.
    
    */
    

для удобства чтения кода здесь, после этого комментария, я определил метку step1 и использовал ее в шаге 2 и 3. На самом деле, в 60+ исходных файлах только эта ситуация и один 4-уровневый вложенный для-это места, которые я использовал goto. Только в двух местах.

все вышеперечисленное действительно, Вы также можете посмотреть, сможете ли вы уменьшить сложность вашего кода и облегчить необходимость в goto, уменьшив количество кода, которое находится в разделе, отмеченном как "много кода" в вашем примере. Дополнительно delete 0 является допустимым оператором C++

использование меток GOTO в C++ - это плохой способ программирования, вы можете уменьшить потребность, выполнив OO Программирование (deconstructors!) и пытается держите процедуры как можно меньше как это возможно.

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

ваша процедура может быть просто писал так:

bool foo()
{
    bool bRetVal = false;
    int p = 0;

    // Calls to various methods that do algorithms on the p integer
    // and give a return value back to this procedure.

    return bRetVal;
}

вы должны попробовать блок catch в основной программе обрабатывает проблемы с памятью, что информирует пользователя о нехватка памяти, что очень редко... (Разве сама ОС не сообщает об этом тоже?)

также обратите внимание, что не всегда есть необходимость использовать указатель, Они полезны только для динамических вещей. (Создание одной вещи внутри метода, не зависящего от ввода из любого места, на самом деле не является динамическим)

Я не собираюсь говорить, что goto всегда плохо, но ваше использование его, безусловно, есть. Такого рода" разделы очистки " были довольно распространены в начале 1990-х годов, но использование его для нового кода-чистое зло.

void MyClass::myFunction()
{
   A* a = new A;
   B* b = new B;
   C* c = new C;
   StartSomeBackgroundTask();
   MaybeBeginAnUndoBlockToo();

   if ( ... )
   {
     goto Exit;
   }

   if ( ... ) { .. }
   else
   {
      ... // what happens if this throws an exception??? too bad...
      goto Exit;
   }

Exit:
  delete a;
  delete b;
  delete c;
  StopMyBackgroundTask();
  EndMyUndoBlock();
}

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

struct MyFunctionResourceGuard
{
  MyFunctionResourceGuard( MyClass& owner ) 
  : m_owner( owner )
  , _a( new A )
  , _b( new B )
  , _c( new C )
  {
      m_owner.StartSomeBackgroundTask();
      m_owner.MaybeBeginAnUndoBlockToo();
  }

  ~MyFunctionResourceGuard()
  {
     m_owner.StopMyBackgroundTask();
     m_owner.EndMyUndoBlock();
  }

  std::auto_ptr<A> _a;
  std::auto_ptr<B> _b;
  std::auto_ptr<C> _c;

};

void MyClass::myFunction()
{
   MyFunctionResourceGuard guard( *this );

   if ( ... )
   {
     return;
   }

   if ( ... ) { .. }
   else
   {
      ...
   }
}

несколько лет назад я придумал псевдо-идиому, которая избегает goto и смутно похожа на обработку исключений в C. Она, вероятно, уже была изобретена кем-то другим, поэтому я думаю, что "обнаружил ее самостоятельно" :)

BOOL foo()
{
   BOOL bRetVal = FALSE;
   int *p=NULL;

   do
   {
       p = new int;
       if(p==NULL)
       {
          cout<<" OOM \n";
          break;
       }

       // Lot of code...

       bRetVal = TRUE;

    } while (false);

   if(p)
   {
     delete p;
     p= NULL;
   }

   return bRetVal;
}

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

Как правило, единственное исключение, которое я делаю из правила "no goto" при программировании, - это выход из вложенных циклов на определенный уровень, что я только столкнулся с необходимостью делать при работе над математическим программированием.

например:

for(int i_index = start_index; i_index >= 0; --i_index)
{
    for(int j_index = start_index; j_index >=0; --j_index)
        for(int k_index = start_index; k_index >= 0; --k_index)
            if(my_condition)
                goto BREAK_NESTED_LOOP_j_index;
BREAK_NESTED_LOOP_j_index:;
}

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

  • функция слишком длинная; рефакторинг некоторого кода в отдельные функции может помочь.

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

  • не пользуясь STL типы, такие как auto_ptr

  • неверная проверка на наличие ошибок, а не ловить исключения. (Я бы сказал, что проверка на OOM бессмысленна на подавляющем большинстве платформ, так как если у вас заканчивается память, у вас есть большие проблемы, чем ваше программное обеспечение может исправить, если вы не пишете саму ОС)

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

использование " GOTO "изменит" логику " программы и то, как вы вводите или как вы представляете, что это будет работать.

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

однако, если мы посмотрим на это на уровне Assmebly, jusing "jump" похож на использование GOTO, и это используется все время, но в сборке вы можете очистить, что вы знаете, что у вас есть в стеке и других регистрах раньше вы проходите дальше.

Итак, при использовании GOTO я бы убедился, что программное обеспечение "появится", поскольку со-кодеры будут вводить, GOTO будет иметь "плохой" эффект на ваше программное обеспечение imho.

Так что это скорее объяснение, почему не использовать GOTO, а не решение для замены, потому что это очень зависит от того, как все остальное построено.

возможно, я что-то пропустил: вы переходите к выходу метки, если P равно null, а затем проверяете, не является ли он null (что не так), чтобы увидеть, нужно ли его удалить (что не обязательно, потому что он никогда не выделялся в первую очередь).

if/goto не будет, и не нужно удалять p. замена goto на return false будет иметь тот же эффект (а затем вы можете удалить метку выхода).

единственные места, которые я знаю, где Гото полезны, похоронены глубоко противные Парсеры (или лексические анализаторы), и в подделке государственных машин (похороненных в массе макросов CPP). В этих двух случаях они были использованы, чтобы сделать очень запутанную логику проще, но это очень редко.

функции (a вызывает a'), Try/Catches и setjmp/longjmps-все это более приятные способы избежать сложной синтаксической проблемы.

Павел.

игнорируя тот факт, что new никогда не вернет NULL, возьмите свой код:

  BOOL foo()
  {
     BOOL bRetVal = FALSE;

     int *p=NULL;

     p = new int;

     if(p==NULL)
     {
        cout<<" OOM \n";
        goto Exit;
     }

     // Lot of code...

  Exit:
     if(p)
     {
        delete p;
        p= NULL;
     }

     return bRetVal;
  }

и написать так:

  BOOL foo()
  {
     BOOL bRetVal = FALSE;

     int *p = new int;

     if (p!=NULL)
     {
        // Lot of code...

        delete p;
     }
     else
     {
        cout<<" OOM \n";
     }

     return bRetVal;
  }