Использовать GOTO или нет? [закрытый]

Неустранимая ошибка: [TraitA] и [TraitB] определяют одно и то же свойство ([$ x]) в композиции [ClassC]

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

Пример:

Проблемное: хотя можно разрешить конфликты между конкурирующими методами , в настоящее время нет синтаксиса, который бы разрешил конфликт между двумя конкурирующими свойствами. Единственное решение в это время - refactor ; т.е. избежать конфликта между именами свойств, которые приводят к фатальной ошибке.


Вопросы, относящиеся:

61
задан 6 revs, 5 users 47% 16 May 2016 в 06:46
поделиться

32 ответа

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

(Отмечают, что в C# и Java, это обычно решается попыткой/наконец)

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

РЕДАКТИРОВАНИЕ : Позвольте мне разрешить это немного.

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

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 или что-то подобное, потому что пример выше, как это, очевидно несовершенен.

60
ответ дан 8 revs, 3 users 89% 24 November 2019 в 16:55
поделиться

Необходимо прочитать эту сводку потока из списков рассылки ядра Linux (обращающий особое внимание на ответы от Linus Torvalds) перед формированием политики для goto:

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

6
ответ дан 2 revs, 2 users 67% 24 November 2019 в 16:55
поделиться

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

Однако Ваш код не будет работать , как Вы ожидаете, потому что это:

p = new int;
if(p==NULL) { … }

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

Что-то подобное верно для Вашего delete блок как Harald, как сказано: if (p) является ненужным перед [1 110].

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

bool foo() // prefer native types to BOOL, if possible
{
    bool ret = false;
    int i;
    // Lots of code.
    return ret;
}
20
ответ дан 2 revs 24 November 2019 в 16:55
поделиться
17
ответ дан Marc Charbonneau 24 November 2019 в 16:55
поделиться

В целом, и на поверхности, нет никакой вещи неправильно с Вашим подходом, при условии, что у Вас только есть одна маркировка, и что 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, если Вы можете.

11
ответ дан 2 revs, 2 users 98% 24 November 2019 в 16:55
поделиться

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

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

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

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

Также примечание в коде C++ там не является никакой потребностью проверить, является ли указатель ПУСТЫМ (обычно, потому что у Вас никогда нет НЕОБРАБОТАННЫХ указателей), но потому что new не возвратит ПУСТОЙ УКАЗАТЕЛЬ (это бросает).

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

8
ответ дан 3 revs, 2 users 77% 24 November 2019 в 16:55
поделиться

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

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

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 правильно), и самое главное, я не должен был писать, что любая очистка кодирует или делает что-либо специальное, чтобы "удостовериться, что мое возвращаемое значение инициализируется".

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

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

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

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

8
ответ дан 3 revs, 2 users 98% 24 November 2019 в 16:55
поделиться

Существует в основном два человека точек, делают в отношении gotos и Вашего кода:

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

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

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

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

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

22
ответ дан 3 revs, 2 users 78% 24 November 2019 в 16:55
поделиться

За эти восемь лет, которые я программировал, я использовал 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 все еще используется, в большой степени мейнфреймовый ассемблер, и программисты, которых я знаю, удостоверяются, что зарегистрировали, где код переходит и почему.

7
ответ дан 3 revs, 3 users 59% 24 November 2019 в 16:55
поделиться

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

5
ответ дан 2 revs 24 November 2019 в 16:55
поделиться

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

59
ответ дан Gene Roberts 24 November 2019 в 16:55
поделиться

Используя маркировки 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;
}

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

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

1
ответ дан Tamara Wijsman 24 November 2019 в 16:55
поделиться

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

1
ответ дан Nemanja Trifunovic 24 November 2019 в 16:55
поделиться

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

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
   {
      ...
   }
}
1
ответ дан Michel 24 November 2019 в 16:55
поделиться

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

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;
}
1
ответ дан ggambett 24 November 2019 в 16:55
поделиться

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

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

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

  • Не использование в своих интересах типы STL, такие как auto_ptr

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

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

1
ответ дан 2 revs, 2 users 77% 24 November 2019 в 16:55
поделиться

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

C++
1
ответ дан Harald Scheirich 24 November 2019 в 16:55
поделиться

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

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

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

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

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

0
ответ дан Filip Ekberg 24 November 2019 в 16:55
поделиться

Игнорирование того, которое новый никогда не будет возвращать ПУСТОЙ УКАЗАТЕЛЬ, берет Ваш код:

  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;
  }
0
ответ дан jussij 24 November 2019 в 16:55
поделиться

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

if/goto не будет, и не должным быть удалять p. Замена goto с ложью возврата имела бы тот же эффект (и затем Вы могли удалить маркировку Выхода).

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

Функции (Вызовы'), Попытка/Выгоды и setjmp/longjmps являются всеми более хорошими способами избежать трудной проблемы синтаксиса.

Paul.

0
ответ дан Paul W Homer 24 November 2019 в 16:55
поделиться

Код, который Вы даете нам, (почти) 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 ;
}

(Отмечают, что новый луг интервал несколько глуп в реальном коде, но можно заменить интервал любым видом объекта, и затем, это имеет больше смысла). Давайте предположим, что у нас есть объект типа T (T, мог быть интервал, некоторый класс 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 имеет много фасетов (т.е. использующий интеллектуальные указатели, стек, с помощью векторов вместо массивов переменной длины, и т.д.), но в целом о записи как можно меньше кода, позволяя компилятору очистить материал в правильный момент.

2
ответ дан 2 revs, 2 users 90% 24 November 2019 в 16:55
поделиться

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

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

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

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

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

3
ответ дан 2 revs, 2 users 80% 24 November 2019 в 16:55
поделиться

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

Вместо...

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;

можно сделать то же с операторами перехода как это:

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. И наконец, некоторые люди не используют исключения в своем коде и дают компиляторам команду отключать их. Таким образом не весь код должен быть безопасным исключением.

5
ответ дан 2 revs 24 November 2019 в 16:55
поделиться

Оборотная сторона GOTO вполне прилично обсуждена. Я просто добавил бы, что 1) иногда Вы должны использовать их и должны знать, как минимизировать проблемы и 2) некоторые принятые методы программирования являются GOTO-скрытыми, так быть осторожными.

1), Когда необходимо использовать GOTO, такой как в ASM или в .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:

В otherwords, цель маркировки не к [1 114], делают , что-то, но к [1 115] перескакивает что-то.

2) то, Что я называю GOTO-скрытыми, является чем-либо, во что можно было превратиться код GOTO+LABELS, просто определив пару макросов. Примером является метод реализации конечных автоматов при наличии переменной состояния и в-то-время-как-оператора-переключения.

 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-меньше кода. Я предпочитаю избегать GOTO-скрытого кода, потому что он может войти в те же проблемы запутанного кода как явный gotos.

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

5
ответ дан 2 revs 24 November 2019 в 16:55
поделиться

Goto обеспечивает лучше , не повторяют себя (DRY), когда "логика заключительной части" характерна для some-but-not-all-cases. Особенно в операторе "переключателя" я часто использую goto's, когда некоторые ответвления переключателя имеют общность заключительной части.

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 не соответствует, действительно необходимо использовать выгоду попытки, но tail-end-merge-don't-repeat-yourself происходит довольно часто, когда безопасность исключения не является проблемой.

, Таким образом, в то время как goto может использоваться для кодирования подобных спагетти структур, в случае последовательности заключительной части, которая характерна для some-but-not-all-cases тогда, goto УЛУЧШАЕТ удобочитаемость кода и даже пригодности для обслуживания, если Вы иначе были бы материалом копии/вставки так, чтобы очень позже кто-то мог бы обновить one-and-not-the-other. Таким образом, это - другой случай, будучи фанатиком о догме может быть контрпродуктивным.

5
ответ дан 4 revs, 2 users 75% 24 November 2019 в 16:55
поделиться

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

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

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

В-третьих, в то время как Вы предлагаете довольно дисциплинированное использование goto, там будет искушением использовать их более свободным способом, и затем код станет трудным понять.

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

4
ответ дан David Thornley 24 November 2019 в 16:55
поделиться

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

4
ответ дан 2 revs, 2 users 75% 24 November 2019 в 16:55
поделиться

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

3
ответ дан Head Geek 24 November 2019 в 16:55
поделиться

Предыдущие комментарии являются всеми серьезными основаниями не использовать goto.

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

0
ответ дан 2 revs, 2 users 67% 24 November 2019 в 16:55
поделиться

Из всех предыдущих комментариев:

  1. goto очень очень плох
  2. , Он делает код трудно, чтобы читать и понять.
  3. Это может вызвать известную проблему "запутанный код"
  4. , Все соглашаются, что это не должно быть сделано.

, Но я использую в следующем сценарии

  1. , Он используется для продвижения и только к одной маркировке.
  2. раздел goto привык к коду очистки и установил возвращаемое значение. Если я не использую goto тогда, я должен создать класс каждого типа данных. Как я должен обернуть интервал * в класс.
  3. Это сопровождается в целом проекте.

я соглашаюсь, что это плохо, но тем не менее это делает вещи намного легче, если сопровождается правильно.

0
ответ дан 2 revs, 2 users 70% 24 November 2019 в 16:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: