C++ для начинающих.

5.3. Инструкция if.

Инструкция if обеспечивает выполнение или пропуск инструкции или блока в зависимости от условия. Ее синтаксис таков:

If ( условие ).

Инструкция.

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

If(a+bc) { ... }.

Или инструкцией объявления с инициализацией:

If ( int ival = compute_value() ){...}.

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

If ( int ival = compute_value() ) {

// область видимости ival.

// ограничена этим блоком.

}

// ошибка: ival невидим.

If ( ! ival ) ...

Попробуем для иллюстрации применения инструкции if реализовать функцию min(), возвращающую наименьший элемент вектора. Заодно наша функция будет подсчитывать число элементов, равных минимуму. Для каждого элемента вектора мы должны проделать следующее:

* Сравнить элемент с текущим значением минимума.

* Если элемент меньше, присвоить текущему минимуму значение элемента и сбросить счетчик в 1.

* Если элемент равен текущему минимуму, увеличить счетчик на 1.

* В противном случае ничего не делать.

* После проверки последнего элемента вернуть значение минимума и счетчика.

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

Вот первый вариант функции min(). Второй аргумент функции будет возвращать количество вхождений минимального значения в вектор. Для перебора элементов массива используется цикл for. Но мы допустили ошибку в логике программы. Сможете ли вы заметить ее?

#include vector.

Int min( const vectorint ivec, int occurs ).

{

Int minVal = 0;

Occurs = 0;

Int size = ivec.size();

For ( int ix = 0; ix size; ++ix ) {

If ( minVal == ivec[ ix ] ).

++occurs;

Else.

If ( minVal ivec[ ix ] ) {

MinVal = ivec[ ix ];

Occurs = 1;

}

}

Return minVal;

}

Обычно функция возвращает только одно значение. Однако согласно нашей спецификации в точке вызова должно быть известно не только само минимальное значение, но и количество его вхождений в вектор. Для возврата второго значения мы использовали параметр типа ссылка. (Параметры-ссылки рассматриваются в разделе 7.3.) Любое присваивание значения ссылке occurs изменяет значение переменной, на которую она ссылается:

Int main().

{

Int occur_cnt = 0;

Vector int ivec;

// occur_cnt получает значение occurs.

// из функции min().

Int minval = min( ivec, occur_cnt );

// ...

}

Альтернативой использованию параметра-ссылки является применение объекта класса pair, представленного в разделе 3.14. Функция min() могла бы возвращать два значения в одной паре:

// альтернативная реализация.

// с помощью пары.

#include uti1ity.

#include vector.

Typedef pairint,int min_va1_pair;

Min_va1_pair.

Min( const vectorint ivec ).

{

Int minVal = 0;

Int occurs = 0;

// то же самое ...

Return make_pair( minVal, occurs );

}

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

Программу можно изменить, инициализировав minVal первым элементом вектора:

Int minVal = ivec[0];

Теперь функция работает правильно. Однако в ней выполняются некоторые лишние действия, снижающие ее эффективность.

// исправленная версия min().

// оставляющая возможность для оптимизации ...

Int minVal = ivec[0];

Occurs = 0;

Int size = ivec.size();

For ( int ix = 0; ix size; ++ix ).

{

If ( minVal == ivec[ ix ] ).

++occurs;

// ...

Поскольку ix инициализируется нулем, на первой итерации цикла значение первого элемента сравнивается с самим собой. Можно инициализировать ix единицей и избежать ненужного выполнения первой итерации. Однако при оптимизации кода мы допустили другую ошибку (наверное, стоило все оставить как было!). Сможете ли вы ее обнаружить?

// оптимизированная версия min(),

// к сожалению, содержащая ошибку...

Int minVal = ivec[0];

Occurs = 0;

Int size = ivec.size();

For ( int ix = 1; ix size; ++ix ).

{

If ( minVal == ivec[ ix ] ).

++occurs;

// ...

Если ivec[0] окажется минимальным элементом, переменная occurs не получит значения 1. Конечно, исправить это очень просто, но сначала надо найти ошибку:

Int minVal = ivec[0];

Occurs = 1;

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

Вот окончательная версия функции min() и программа main(), проверяющая ее работу:

#include iostream.

#include vector.

Int min( const vector int ivec, int occurs ).

{

Int minVal = ivec[ 0 ];

Occurs = 1;

Int size = ivec.size();

For ( int ix = 1; ix size; ++ix ).

{

If ( minVal == ivec[ ix ] ).

++occurs;

Else.

If ( minVal ivec[ ix ] ){

MinVal = ivec[ ix ];

Occurs = 1;

}

}

Return minVal;

}

Int main().

{

Int ia[] = { 9,1,7,1,4,8,1,3,7,2,6,1,5,1 };

Vectorint ivec( ia, ia+14 );

Int occurs = 0;

Int minVal = min( ivec, occurs );

Cout "Минимальное значение: " minVal.

" встречается: " occurs " раз.\n";

Return 0;

}

Результат работы программы:

Минимальное значение: 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 ).

Cout "ival = 0\n";

(d) if ( ival = 0 ).

Ival = get_value();

(e) if ( iva1 == 0 ).

Else ival = 0;

Упражнение 5.4.

Преобразуйте тип параметра occurs функции min(), сделав его не ссылкой, а простым объектом. Запустите программу. Как изменилось ее поведение?