Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

188

string *ptr1;

Эквивалентная пара инструкций не позволит допустить такую ошибку: string *ptr2;

В наших примерах мы обычно группируем определения объектов в инструкции по

int aCnt=0, eCnt=0, iCnt=0, oCnt=0, uCnt=0;

сходству употребления. Например, в следующей паре int charCnt=0, wordCnt=0;

первая инструкция объявляет пять очень похожих по назначению объектов счетчиков пяти гласных латинского алфавита. Счетчики для подсчета символов и слов определяются во второй инструкции. Хотя такой подход нам кажется естественным и удобным, нет никаких причин считать его хоть чем-то лучше других.

Упражнение 5.1

Представьте себе, что вы являетесь руководителем программного проекта и хотите, чтобы применение инструкций объявления было унифицировано. Сформулируйте правила использования объявлений объектов для вашего проекта.

Упражнение 5.2

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

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

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

if ( условие )

зависимости от условия. Ее синтаксис таков:

инструкция

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

if(a+b>c) { ... }

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

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

С++ для начинающих

189

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

if ( int ival = compute_value() ) {

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

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

}

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

компиляции:

if ( ! ival ) ...

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

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

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

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

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

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

if ( minVal > ivec[ i ] )...// новое значение minVal

Необходимо использовать две инструкции if:

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-инструкция (точное положение открывающей фигурной скобки является поводом для бесконечных споров):

С++ для начинающих

190

if ( minVal > ivec[ i ] )

{

minVal = ivec[ i ]; occurs = 1;

}

if ( minVal == ivec [ i ] )

Вторая инструкция if выглядит так:

++occurs;

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

if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1;

}

//если minVal только что получила новое значение,

//то occurs будет на единицу больше, чем нужно

if

( minVal == ivec[ i ] )

будет ошибаться на 1:

++occurs;

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

if ( условие ) инструкция1

else

if-else, второй формой if-инструкции. Ее синтаксис выглядит таким образом:

инструкция2

инструкция1 выполняется, если условие истинно, иначе переходим к инструкция2.

if ( minVal == ivec[ i ] ) ++occurs;

else

if ( minVal > ivec[ i ] ) { minVal = ivec[ i ]; occurs = 1;

Например:

}

= ivec[ i ]; = 1;

С++ для начинающих

191

Здесь инструкция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?

if ( minVal <= ivec[ i ] )

if ( minVal == ivec[ i ] ) ++occurs;

else {

minVal= ivec[ i ]; occurs= 1;

(Эту проблему иногда называют проблемой висячего else). Например:

}

Судя по отступам, программист предполагает, что else относится к самому первому, внешнему if. Однако в С++ неоднозначность висячих else разрешается соотнесением их с последним встретившимся if. Таким образом, в действительности предыдущий

if ( minVal <= ivec[ i ] ) { if ( minVal == ivec[ i ] )

++occurs; else {

minVal occurs

}

фрагмент означает следующее:

}

Одним из способов разрешения данной проблемы является заключение внутреннего if в фигурные скобки:

С++ для начинающих

192

if ( minVal <= ivec[ i ] ) { if ( minVal == ivec[ i ] )

++occurs;

}

else {

minVal= ivec[ i ]; occurs= 1;

}

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

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

#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 ) { if ( minVal == ivec[ ix ] )

++occurs; else

if ( minVal > ivec[ ix ] ) { minVal = ivec[ ix ]; occurs = 1;

}

}

return minVal;

ли вы заметить ее?

}

Обычно функция возвращает только одно значение. Однако согласно нашей

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

С++ для начинающих

193

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 pair<int,int> min_va1_pair;

min_va1_pair

min( const vector<int> &ivec )

{

int minVal = 0; int occurs = 0;

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

return make_pair( minVal, occurs );

в одной паре:

}

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

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

int minVal = ivec[0];

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

С++ для начинающих

194

//исправленная версия 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 не получит

int minVal = ivec[0];

значения 1. Конечно, исправить это очень просто, но сначала надо найти ошибку: occurs = 1;

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

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

С++ для начинающих

195

#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 }; vector<int> ivec( ia, ia+14 );

int occurs = 0;

int minVal = min( ivec, occurs );

cout << "Минимальное значение: " << minVal

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

}

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

Минимальное значение: 1 встречается: 5 раз.

В некоторых случаях вместо инструкции if-else можно использовать более краткое и

template <class valueType> inline const valueType&

min( valueType &vall, valueType &va12 )

{

if ( vall < va12 ) return vall;

return va12;

выразительное условное выражение. Например, следующую реализацию функции min():

}

можно переписать так:

С++ для начинающих

196

 

 

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

Исправьте ошибки в примерах: