Правила программирования на С и С++. Главы 7-8 - Избегайте goto, за исключением...
ОГЛАВЛЕНИЕ
89. Избегайте goto, за исключением...
Правила в этом разделе применяйте только к программам на С. Оператор goto не должен никогда использоваться в С++ по причинам, рассмотренным в правиле 54 - существует вероятность того, что конструкторы и деструкторы будет невозможно вызвать.
Вообще вы должны избегать оператора goto не потому, что goto - унаследованный порок, а потому что существуют лучшие решения. Язык С , например, дает вам массу отличающихся от goto способов выхода из циклов.
Оператор goto может также ухудшать читаемость. Я на самом деле видел код, подобный нижеследующему, и чтобы разобраться, как он работает, потребовалось полчаса:
while ( 1 ){
while ( условие ){
// ...
while ( другое_условие ){
метка1:
// ...goto метка2;
}// ...}
if ( третье_условие )
{
// ...
if ( другое_условие )goto метка1;
else
{
метка2:
// ... }}}Но самое интересное, что после того, как я разобрался с этим, стало легко переписать его, исключив переходы goto.Проблема читаемости все же сохраняется, даже если goto в явном виде отсутствует. Оператор switch, например, неявно выполняет goto для перехода к оператору case. Последующий пример вполне законен с точки зрения С, но я не стал бы его вам рекомендовать:
switch( некоторое_условие ){
case A: if ( некоторое_другое_условие )
// ...else{
case b: // ... }}Оператор goto полезен в некоторых случаях. Вот два из них:- Множество переходов goto к единственной метке, стоящей перед оператором return, лучше, чем множество операторов return. Такую процедуру легче отлаживать, так как для перехвата выхода из нее вы можете установить единственную точку прерывания. Имейте в виду, что goto должен предшествовать оператору; он не может стоять перед закрывающей фигурной скобкой. При необходимости пользуйтесь следующим приемом:
exit:
return ;
}
- Переходы goto вниз по программе, обеспечивающие выход из системы вложенных циклов, лучше, чем флаг завершения типа "готов", который должен проверяться в каждом операторе управления циклом. Если каждый из операторов while в следующем примере выполнить по 100 раз, то флаг "готов" нужно проверить 1000000 раз, хотя он установлен всего лишь на случай ошибки:
int условие1, условие2, условие3;
// ...
while ( !готов ?? условие1 )
{
while ( !готов ?? условие2 ) {while ( !готов ?? условие3 )
{
if ( нечто_ужасное )
готов = 1;}}}Исключите миллионы ненужных проверок при помощи goto следующим образом: while ( условие1 ){
while ( условие2 ){
while ( условие3 ){
if ( нечто_ужасное )
goto выход;}}}выход:
// ...Проверка в операторе управления циклом - единственное место, где эффективность действительно является важным обстоятельством, потому что код выполняется многократно. Это особенно верно для внутренних операторов управления вложенных циклов. Проверка флага завершения во внутреннем цикле может существенно замедлить выполнение, и ее лучше избегать.