Раздел 2.8
въвежда класа IntArray. Ето реализацията на член-функцията min() на
IntArray:
#include "IntArray.h"IntArray:
min( int &occurs
){
int minVal = ia[ 0 ];
occurs = 1;
for ( int i = 1; i < size; ++i
) {
if ( minVal == ia[ i ] )++occurs;
elseif ( minVal > ia[ i ] )
{
minVal = ia[ i ];occurs = 1;
}}
return minVal;}
За да
върне както минималната стойност, така и броя на появяванията й, min() използува
един аргумент от тип указател към integer. Аргументът от тип указател подава
стойността за запис на фактическия аргумент на функцията; аргумент, който не е
от тип указател подава стойност за четене към функцията.
Подаването-на-аргумент-чрез-указател позволява да му бъде присвоявана стойност
от вътрешността на функцията. Ето един пимер за използуването
му:
#include <stream.h>
#include "IntArray.h"
IntArray
myArray;
main() {
// initialize the array in descending order
for ( int
i = myArray.getSize()-2; i >= 0; --i )
myArray[ i ] = i;// insert a second
copy of 0q the lowest value
myArray[ myArray=getSize()-1 ] = 0;
int number
= 0;
int low = myArray.min( number );
cout << "nlow: " <<
low<< "number:
number << "n";
}
Когато тази програма
бъде компилирана и изпълнена, се получава следния резултат:
low:
0
number:
2
Раздел 4.7 разглежда типа указател и
подаването-на-аргумент-чрез-указател по-подробно.
Упражнение 3-13.
Променете декларацията на occurs от списъка на аргументите на min(), така че да
не бъде от тип указател и изпълнете програмата отново.
3.14.
Операторът switch
Няколко вложени if-else оператори често може да
бъдат синтактично правилни и въпреки това да не изразяват правилно намеренията
на програмиста. Например, едно свързване на if със else клаузата, което
противоречи на намеренията на програмиста, често може да мине незабелязано.
Някои промени на оператори също могат да се окажат източници на грешки. С++
предлага един алтернативен метод за избор между множество от взаимноизключващи
се възможности. Това е оператора switch. Например, да предположим, че ни се
налага да преброим броя на появите на всяка от петте гласни в случайно избран
откъс от текст. (Традиционното мнение е, че “е” е най-често срещаната гласна).
Нашата програма ще има следния алгоритъм:
чете последователно всеки
символ дотогава, докато няма повече символи за четене;
сравнява всеки символ
с множеството от гласни;
ако символът е гласна добавя 1 към брояча
й;
показва резултатите.
Програмата беше използувана за анализиране на
предишния раздел на тази книга. Ето резултатите от изпълнението й, които
потвърждават традиционното мнение относно честотата на поява на гласната
“е”:
aCnt:
394
eCnt:
721
iCnt:
461
oCnt:
349
uCnt:
186
Програмата
е реализирана като оператор swith с пет разклонения за всяка гласна. В оператора
switch различните условия се отбелязват чрез етикета case. Изброяването на
гласните се използува за да бъде увеличена читаемостта на програмата. Ето и
самата програма:
#include <stream.h>
enum Vowels { a = ‘a’,
e = ‘e’, i = ‘i’,o = ‘o’, u = ‘u’ };
main() {
char ch;
int aCnt=0,
eCnt=0, iCnt=0, oCnt=0, uCnt=0;
while ( cin >> ch )switch ( ch )
{
case a: ++aCnt;break;
case e: ++eCnt;break;
case i:
++iCnt;break;
case o: ++oCnt;break;
case u: ++uCnt;break;
}; //
end
switch(ch)
cout << "aCnt: t" << aCnt <<
"n";
cout << "eCnt: t" << eCnt << "n";
cout <<
"iCnt: t" << iCnt << "n";
cout << "oCnt: t" <<
oCnt << "n";
cout << "uCnt: t" << uCnt <<
"n";
}
Съществува, обаче, един проблем, свързан с логиката на
програмата. Например, как би обработила програмата следните входни данни? UNIX
Главните букви U и I не могат да бъдат разпознати като гласни. Нашата програма
не е в състояние да брои гласните, които са записани с главни букви. Преди да
поправим програмата си, обаче, нека разгледаме по-внимателно оператора switch.
Стойността, записана след ключовата дума case, която се нарича case - етикет,
трябва да завършва с двоеточие. Всеки case-етикет трябва да съдържа израз от тип
integer. Два case-етикета не могат да имат една и съща стойност; иначе по време
на компилация ще бъде отбелязана грешка.
Когато се изпълнява оператора
switch, тестовият му израз се сравнява всеки от case-етикетите последователно.
Ако не може да бъде намерено съответствие между израза и множеството от етикети
не се изпълнява нищо.
Съществува една обща неправилна представа, че ще
бъдат изпълнени само операторите, записани след съответния case-етикет. По-скоро
може да се каже, че изпълнението започва там, продължава през тялото и приключва
в края на оператора switch. Нека разгледаме по-подробно тази друга обща причина
за програмни грешки. Ето нашия предишен оператор switch, малко
променен:
switch ( ch ){
case a: ++aCnt;
case e: ++eCnt;
case i:
++iCnt;
case o: ++oCnt;
case u: ++uCnt;}; // end switch(ch)
cout
<< ch << "n";// for illustration
Ако ch има стойност 1
изпълнението започва от етикета i:. ICnt се увеличава. Изпълнението, обаче, не
спира до тук, а продължава през границата на следващия case до затварящата скоба
на оператора switch. По такъв начин се увеличават и oCnt, и uCnt. Ако следващата
стойност на ch е “e”, то ще бъдат увеличени eCnt, iCnt, oCnt и
uCnt.
Програмистът явно трябва да укаже на компилатора да прекрати
изпълнението на операторите от тялото на оператора switch. Обикновено,
последният оператор след един case-етикет е break.
Когато бъде срещнат
оператора break се прекратява изпълнението на оператора switch. Управлението се
предава на операторите, непосредствено следващи затварящата скоба на switch. В
нашия пример този оператор е:
cout << ch << "n"; // for
illustration
Към етикет, за който съзнателно е пропуснат оператора break,
в повечето случаи непременно трябва да бъде добавен коментар, че това пропускане
е обмислено. Ако от контекста, обаче, става ясно защо е направено това,
коментарът може да бъде пропуснат. Кога е възможно програмистът да иска да
пропусне оператора break от case-етикета? Една възможност има, когато дадено
множество от стойности трябва да бъде обработено по един и същ начин. Всеки
елемент от това множество трябва да бъде представен със собствен case-етикет.
Например, да припомним, че предишната ни програма не разпознаваше гласните,
записани с главни букви - ето правилната реализация:
swithc (ch)
{
case A:
case a: ++aCnt;break;// ...case U:
case u:
++uCnt;break;
};
Операторът switch предлага еквивалент на безусловната
else клауза. Това е етикета default. Ако никой от етикетите не съответствува на
израза-тест и е зададен етикет default ще бъдат изпълнени операторите след него.
В нашата програма бихме могли да изчисляваме броя на съгласните:
#include
( ch ) {
//...
switch (ch) {
case A:
case a: ++aCnt;break; //
...
case U:
case u: ++uCnt;break;
default: if isalpha( ch
)++conCnt;
break;
};
isalpha() е функция, записана в С библиотека;
тя връща стойност истина ако аргументът й е буква от азбуката. За да я
използува, програмистът трябва да включи системния заглавен файл
ctype.h.
Въпреки, че не е задължително добавянето на оператора break след
последния етикет, препоръчва се написването му. Ако в последствие бъде написан
допълнителен case-етикет в края на оператора switch, липсата на break може
внезапно да се окаже съществена.
Упражнение 3-14. Модифицирайте
програмата така, че да бъдат броени и прочетените интервали.
Упражнение
3-15. Изменете програмата така, че да бъдат броени следните двубуквени
последователности:
ff, f1 и fi.
Итеративни оператори
Много
програмни действия изискват прилагането на определен брой оператори върху
съвкупност от подобни обекти. Една банкова програма може да бъде написана за да
обработва чекове. За всеки чек програмата трябва:
- да чете съдържанието
му,
- да провери дали той има правилен счетоводен номер,
- да провери дали
наличната сума е достатъчна да покрие чековата стойност,
- да дебитира
сметката за сумата на чека и
- да запише измененията.
Едва ли е
разумно тази кодова последователност да бъде повтаряна за всеки чек. Естествено,
езикът предлага оператори за управление на итерации, наречени цикли, които
позволяват на програмата да описва многократно изпълнение на определен набор от
оператори.
Един цикъл изпълнява операторите от тялото си докато
определено условие остава истина. Банковата програма вероятно ще си дефинира
следното условие:
while there exists a check to be
processed
докато има чекове за обработване
Изпълнението на цикъла
ще приключи, когато бъде обработен последния чек; т.е. когато дефинираното
условие получи стойност лъжа. Това условие понякога се нарича управляващо
цикъла.
С++ поддържа три конструкции за цикъл: операторите while, do и
for. Основната разлика между тях е в метода за управление на цикъла. И за трите
оператора истинно условие е всяка ненулева стойност. Нулевата стойност е
еквивалентна на невярно управляващо условие.