5.3. Инструкция ifИнструкция if обеспечивает выполнение или пропуск инструкции или блока в зависимости от условия. Ее синтаксис таков: if ( условие ) инструкция условие заключается в круглые скобки. Оно может быть выражением, как в этом примере: if(a+b>c) { ... } или инструкцией объявления с инициализацией: if ( int ival = compute_value() ){...} Область видимости объекта, объявленного в условной части, ограничивается ассоциированной с if инструкцией или блоком. Например, такой код вызывает ошибку компиляции: if ( int ival = compute_value() ) { // область видимости ival // ограничена этим блоком } // ошибка: ival невидим Попробуем для иллюстрации применения инструкции if реализовать функцию min(), возвращающую наименьший элемент вектора. Заодно наша функция будет подсчитывать число элементов, равных минимуму. Для каждого элемента вектора мы должны проделать следующее:
Необходимо использовать две инструкции if: if ( minVal > ivec[ i ] )...// новое значение minVal if ( minVal == ivec[ i ] )...// одинаковые значения Довольно часто программист забывает использовать фигурные скобки, если нужно выполнить несколько инструкций в зависимости от условия: if ( minVal > ivec[ i ] ) minVal = ivec[ i ]; occurs = 1; // не относится к if! Такую ошибку трудно увидеть, поскольку отступы в записи подразумевают, что и minVal=ivec[i], и occurs=1 входят в одну инструкцию if. На самом же деле инструкция occurs = 1; не является частью if и выполняется безусловно, всегда сбрасывая occurs в 1. Вот как должна быть составлена правильная if-инструкция (точное положение открывающей фигурной скобки является поводом для бесконечных споров): if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1; } Вторая инструкция if выглядит так: if ( minVal == ivec [ i ] ) ++occurs; Заметим, что порядок следования инструкций в этом примере крайне важен. Если мы будем сравнивать minVal именно в такой последовательности, наша функция всегда будет ошибаться на 1: if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1; } // если minVal только что получила новое значение, // то occurs будет на единицу больше, чем нужно if ( minVal == ivec[ i ] ) ++occurs; Выполнение второго сравнения не обязательно: один и тот же элемент не может одновременно быть и меньше и равен minVal. Поэтому появляется необходимость выбора одного из двух блоков в зависимости от условия, что реализуется инструкцией if-else, второй формой if-инструкции. Ее синтаксис выглядит таким образом: if ( условие ) инструкция1 else инструкция2 инструкция1 выполняется, если условие истинно, иначе переходим к инструкция2. Например: if ( minVal == ivec[ i ] ) ++occurs; else if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1; } Здесь инструкция2 сама является if-инструкцией. Если minVal меньше ivec[i],
никаких действий не производится. if ( minVal < ivec[ i ] ) {} // пустая инструкция else if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1; } else // minVal == ivec[ i ] ++occurs; Составные инструкции if-else могут служить источником неоднозначного толкования, если частей else больше, чем частей if. К какому из if отнести данную часть else? (Эту проблему иногда называют проблемой висячего else). Например: if ( minVal <= ivec[ i ] ) if ( minVal == ivec[ i ] ) ++occurs; else { minVal = ivec[ i ]; occurs = 1; } Судя по отступам, программист предполагает, что else относится к самому первому, внешнему if. Однако в С++ неоднозначность висячих else разрешается соотнесением их с последним встретившимся if. Таким образом, в действительности предыдущий фрагмент означает следующее: if ( minVal <= ivec[ i ] ) { if ( minVal == ivec[ i ] ) ++occurs; else { minVal = ivec[ i ]; occurs = 1; } } Одним из способов разрешения данной проблемы является заключение внутреннего if в фигурные скобки: if ( minVal <= ivec[ i ] ) { if ( minVal == ivec[ i ] ) ++occurs; } else { minVal = ivec[ i ]; occurs = 1; } В некоторых стилях программирования рекомендуется всегда употреблять фигурные
скобки при использовании инструкций if-else, чтобы не допустить возможности
неправильной интерпретации кода. #include <vector> int min( const vector<int> &ivec, int &occurs ) { int minVal = 0; occurs = 0; int size = ivec.size(); for ( int ix = 0; ix < size; ++ix ) { Обычно функция возвращает только одно значение. Однако согласно нашей спецификации в точке вызова должно быть известно не только само минимальное значение, но и количество его вхождений в вектор. Для возврата второго значения мы использовали параметр типа ссылка. (Параметры-ссылки рассматриваются в разделе 7.3.) Любое присваивание значения ссылке occurs изменяет значение переменной, на которую она ссылается: int main() { int occur_cnt = 0; vector< int > ivec; // occur_cnt получает значение occurs Альтернативой использованию параметра-ссылки является применение объекта класса pair, представленного в разделе 3.14. Функция min() могла бы возвращать два значения в одной паре: // альтернативная реализация // с помощью пары #include <uti1ity> #include <vector> typedef pair<int,int> min_va1_pair; min_va1_pair К сожалению, и эта реализация содержит ошибку. Где же она? Правильно: мы инициализировали
minVal нулем, поэтому, если минимальный элемент вектора больше нуля, наша реализация
вернет нулевое значение минимума и нулевое значение количества вхождений. int minVal = ivec[0]; Теперь функция работает правильно. Однако в ней выполняются некоторые лишние действия, снижающие ее эффективность. // исправленная версия min() // оставляющая возможность для оптимизации ... int minVal = ivec[0]; Поскольку ix инициализируется нулем, на первой итерации цикла значение первого элемента сравнивается с самим собой. Можно инициализировать ix единицей и избежать ненужного выполнения первой итерации. Однако при оптимизации кода мы допустили другую ошибку (наверное, стоило все оставить как было!). Сможете ли вы ее обнаружить? // оптимизированная версия min(), // к сожалению, содержащая ошибку... int minVal = ivec[0]; Если ivec[0] окажется минимальным элементом, переменная occurs не получит значения 1. Конечно, исправить это очень просто, но сначала надо найти ошибку: int minVal = ivec[0]; occurs = 1; К сожалению, подобного рода недосмотры встречаются не так уж редко: программисты
тоже люди и могут ошибаться. Важно понимать, что это неизбежно, и быть готовым
тщательно тестировать и анализировать свои программы. #include <iostream> #include <vector> int min( const vector< int > &ivec, int &occurs ) Результат работы программы: Минимальное значение: 1 встречается: 5 раз. В некоторых случаях вместо инструкции if-else можно использовать более краткое и выразительное условное выражение. Например, следующую реализацию функции min(): template <class valueType> inline const valueType& min( valueType &vall, valueType &va12 ) { if ( vall < va12 ) return vall; return va12; } можно переписать так: template <class valueType> inline const valueType& min( valueType &vall, valueType &va12 ) { return ( vall < va12 ) ? vall : va12; } Длинные цепочки инструкций if-else, подобные приведенной ниже, трудны для восприятия и, таким образом, являются потенциальным источником ошибок. if ( ch == 'a' || ch == 'A' ) ++aCnt; else if ( ch == 'e' || ch == 'E' ) ++eCnt; else if ( ch == 'i' || ch == 'I' ) ++iCnt; else if ( ch == 'o' || ch == '0' ) ++oCnt; else if ( ch == 'u' || ch == 'U' ) ++uCnt; В качестве альтернативы таким цепочкам С++ предоставляет инструкцию switch. Это тема следующего раздела. Упражнение 5.3Исправьте ошибки в примерах: (a) if ( ivall != iva12 ) ivall = iva12 else ivall = iva12 = 0; (b) if ( ivat < minval ) minvat = ival; occurs = 1; (c) if ( int ival = get_value()) cout << "ival = " << ival << endl; if ( ! ival ) (d) if ( ival = 0 ) ival = get_value(); (e) if ( iva1 == 0 ) else ival = 0; Упражнение 5.4Преобразуйте тип параметра occurs функции min(), сделав его не ссылкой, а простым объектом. Запустите программу. Как изменилось ее поведение? Назад ВпередСодержание |
Нет комментариев. Оставить комментарий: |