Некоторые говорят, что нет причины для перехода в C ++. Некоторые говорят, что в 99% случаев есть лучшие альтернативы. Это не рассуждения, а просто иррациональные впечатления. Вот хороший пример, где goto приводит к хорошему коду, что-то вроде расширенного цикла do-while:
int i;
PROMPT_INSERT_NUMBER:
std::cout << "insert number: ";
std::cin >> i;
if(std::cin.fail()) {
std::cin.clear();
std::cin.ignore(1000,'\n');
goto PROMPT_INSERT_NUMBER;
}
std::cout << "your number is " << i;
Сравните это с кодом goto-free:
int i;
bool loop;
do {
loop = false;
std::cout << "insert number: ";
std::cin >> i;
if(std::cin.fail()) {
std::cin.clear();
std::cin.ignore(1000,'\n');
loop = true;
}
} while(loop);
std::cout << "your number is " << i;
Я вижу эти различия:
- требуется вложенный
{}
блок (хотя do {...} while
выглядит более знакомым)
- дополнительная
loop
требуется переменная, используется в четырех местах
- требуется больше времени, чтобы прочитать и понять работу с
loop
-
loop
не содержит никаких данных, он просто контролирует поток выполнения, который менее понятен, чем простая метка
Есть еще один пример
void sort(int* array, int length) {
SORT:
for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
swap(data[i], data[i+1]);
goto SORT; // it is very easy to understand this code, right?
}
}
Теперь давайте избавимся от "злого" перехода:
void sort(int* array, int length) {
bool seemslegit;
do {
seemslegit = true;
for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
swap(data[i], data[i+1]);
seemslegit = false;
}
} while(!seemslegit);
}
Вы видите, что это тот же тип использования goto, это хорошо структурированный паттерн, и он не продвигает goto так, как многие продвигают, как единственный рекомендуемый способ. Конечно, вы хотите избежать «умного» кода, подобного этому:
void sort(int* array, int length) {
for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
swap(data[i], data[i+1]);
i = -1; // it works, but WTF on the first glance
}
}
Дело в том, что goto можно легко использовать неправильно, но само goto не виновато. Обратите внимание, что метка имеет область действия функции в C ++, поэтому она не загрязняет глобальную область видимости, как в чистой сборке, в которой перекрывающиеся циклы имеют свое место и очень распространены - как в следующем коде для 8051, где 7-сегментное отображение подключен к P1. Программа зацикливает молниеносный сегмент вокруг:
; P1 states loops
; 11111110 <-
; 11111101 |
; 11111011 |
; 11110111 |
; 11101111 |
; 11011111 |
; |_________|
init_roll_state:
MOV P1,#11111110b
ACALL delay
next_roll_state:
MOV A,P1
RL A
MOV P1,A
ACALL delay
JNB P1.5, init_roll_state
SJMP next_roll_state
Есть еще одно преимущество: goto может служить именованными циклами, условиями и другими потоками:
if(valid) {
do { // while(loop)
// more than one page of code here
// so it is better to comment the meaning
// of the corresponding curly bracket
} while(loop);
} // if(valid)
Или вы можете использовать эквивалентное goto с отступом, поэтому вам не нужно комментировать, если вы правильно выберете название метки:
if(!valid) goto NOTVALID;
LOOPBACK:
// more than one page of code here
if(loop) goto LOOPBACK;
NOTVALID:;