4)
32.973.26
45
Arnold Willemer
Einstieg in C++
© 2012 by Galileo Press Galileo Computing is an imprint of Galileo Press,
Bonn (Germany), Boston (USA) German Edition first published 2012 by Galileo Press.
Titel of the German Edition “Einstieg in C++” ISBN of the German Edition
978-3-8362-1385-1 All rights reserved. Neither this publication nor any part of it may
be copied or reproduced in any form or by any means or translated into another language,
without the prior consent of Galileo Press, Rheinwerkallee 4, 53227 Bonn, Germany.
Galileo Press makes no warranties or representations with respect to the content hereof
and specifically disclaims any implied warranties of merchantability or fitness
for any partiand specifically disclaims any implied warranties of merchantability
or fitness for any particular purpose. Galileo Press assumes no responsibility
for any errors that may appear in this publication.
.
45
++ / .
; [. . . . -
]. — . : , 2013. — 528 . + CD. — (
).
ISBN 978-5-699-65451-2
, ! "# $, ! "!
%
++
&-
% ' %$, * '
.
"*% <
%$,
$ ,
*
, =
,
&
$,
!
%
< . -
*
%
$
% *
< (STL).
" *
> $
%$
*"
> %$
%.
004.42(075.4)
32.973.26
$
%
> *" %%% $
"?
> &
.
@
% ! %?
$*
%
> F%> #
$*
&
*
, "* '
>
!
, !% &
$
,
'
$<
% HHH «J$* « ».
Предисловие .................................................................................................................................................... 12
Глава 1. Введение в программирование .............................................................................................. 14
1.1. Программирование...................................................................................................................... 14
Старт программы ............................................................................................................... 14
1.1.1. Ввод, компиляция, запуск ................................................................................... 16
1.1.2. Алгоритм .................................................................................................................... 17
1.1.4. Язык C++ ................................................................................................................... 18
1.1.5. Контрольные вопросы .......................................................................................... 23
1.2. Основа программы ...................................................................................................................... 23
1.2.1. Комментарии ............................................................................................................ 24
1.2.2. Команды ..................................................................................................................... 27
1.2.3. Блоки ........................................................................................................................... 27
1.3. Переменные .................................................................................................................................... 28
1.3.1. Объявление переменных ..................................................................................... 29
1.3.2. Область действия ................................................................................................... 30
1.3.3. Правила именования и синтаксис .................................................................... 32
1.3.4. Типы переменных ................................................................................................... 34
1.3.5. Синтаксис объявления переменных................................................................ 46
1.3.6. Константы ................................................................................................................. 47
1.4. Обработка ........................................................................................................................................ 56
1.4.1. Присваивание........................................................................................................... 57
1.4.2. Мастер вычислений ............................................................................................... 58
1.4.3. Сокращения .............................................................................................................. 59
1.4.4. Понятие «функция» на примере функции случайных чисел ................ 62
1.4.5. Преобразование типа ............................................................................................ 64
1.5. Ввод и вывод .................................................................................................................................. 65
1.5.1. Потоковый вывод командой cout .................................................................... 66
1.5.2. Форматированный вывод.................................................................................... 68
1.5.3. Потоковый ввод командой cin.......................................................................... 68
1.6. Практические задания ............................................................................................................... 69
Глава 2. Циклическое программное управление ............................................................................. 70
2.1. Ветвление......................................................................................................................................... 71
2.1.1. По условию: if ........................................................................................................ 71
2.1.2. Иначе: else ............................................................................................................... 73
2.1.3. Вариант за вариантом: switch case ............................................................... 77
2.1.4. Короткая проверка с помощью символа вопросительного знака............... 82
2.2. Булевы выражения...................................................................................................................... 83
2.2.1. Переменные и константы .................................................................................... 84
2.2.2. Операторы ................................................................................................................. 84
2.2.3. Объединение булевых выражений .................................................................. 87
5
Оглавление
6
Оглавление
7
Оглавление
6.2. Препроцессор...............................................................................................................................289
6.2.1. Связывание файлов: #include ........................................................................290
6.2.2. Константы и макросы: #define .........................................................................290
6.2.3. Опросы: #if ............................................................................................................293
6.2.4. Предопределенные макросы ............................................................................294
6.2.5. Другие препроцессорные команды ................................................................295
6.3. Разделение исходного кода....................................................................................................296
6.3.1. Пример: игра «Бермуда» ....................................................................................296
6.3.2. Распознавание файлов........................................................................................299
6.3.3. Объявление и определение ...............................................................................300
6.3.4. Заголовочные файлы ..........................................................................................301
6.3.5. Статические функции .........................................................................................303
6.3.6. Скрытая реализация ............................................................................................303
6.4. Компоновщик и библиотеки.................................................................................................305
6.4.1. Подключение статических библиотек ..........................................................305
6.4.2. Динамические библиотеки ...............................................................................306
6.5. Программа make ..........................................................................................................................309
6.5.1. Макросы в make-файле ......................................................................................312
6.5.2. Несколько строк....................................................................................................314
6.6. Отладка с помощью GNU Debugger ..................................................................................314
Глава 7. Другие элементы языка C++ ................................................................................................317
7.1. Обобщенное программирование .........................................................................................317
7.1.1. Шаблонные функции ..........................................................................................318
7.1.2. Шаблоны классов .................................................................................................321
7.1.3. Макропрограммирование с помощью команды #define ........................325
7.2. Пространство имен....................................................................................................................327
7.2.1. Определение пространства имен ....................................................................328
7.2.2. Доступ .......................................................................................................................329
7.2.3. Особые пространства имен ...............................................................................329
7.2.4. Анонимное пространство имен .......................................................................330
7.2.5. Граф синтаксиса ....................................................................................................331
7.3. Защита от сбоев при помощи ключевых слов try и catch .....................................332
7.3.1. Создание собственных исключительных ситуаций ................................333
7.3.2. Разработка классов ошибок ..............................................................................337
7.3.3. Исключения стандартных библиотек ...........................................................341
7.4. Низкоуровневое программирование .................................................................................347
7.4.1. Битовые операторы..............................................................................................347
7.4.2. Операторы сдвига .................................................................................................350
7.4.3. Доступ по аппаратным адресам.......................................................................351
7.4.4. Битовые структуры ..............................................................................................352
Глава 8. Библиотеки ...................................................................................................................................354
8.1. Символьные последовательности и строки ...................................................................354
8.1.1. Стандартный класс string ...............................................................................355
8.1.2. Другие библиотеки строк ..................................................................................368
8.1.3. Классические функции языка C .....................................................................369
8.2. Класс iostream для продвинутых......................................................................................376
8.2.1. Ввод командой cin ...............................................................................................376
8.2.2. Манипуляторы ......................................................................................................378
8
Оглавление
9
Оглавление
Читайте с удовольствием!
Штефан Маттешек,
Галилео Компьютинг
ПРЕДИСЛОВИЕ
Я же писал эту книгу так, чтобы она была легко понятна. Если кто-
то хочет выучить язык C++, он должен следовать наглядным примерам
программ, написанных на C++. А когда начинающий уже получил ба-
зовые знания, он сможет найти здесь ответы на вопросы, которые воз-
никают при работе с C++. Я не люблю книги, которые заканчиваются
на полуслове.
1 Бьерн Страуструп. Язык программирования С++. М.: Бином, 2011. Прим. ред.
12
Предисловие
Благодарности
Арнольд Виллемер,
Норгардхольц
Глава 1
ВВЕДЕНИЕ В ПРОГРАММИРОВАНИЕ
Программирование — вытачивание монумента
без пыли и осколков.
1.1. Программирование
Программа принимает вводимые пользователем данные, обрабаты-
вает их заранее определенным способом, который закладывается про-
граммистом, и выдает результат. Действия пользователя всегда огра-
ничены рамками, заданными программистом. Когда вы сами начинаете
программировать, вы берете на себя контроль над компьютером. До
этого можно было делать только то, что позволяло программное обе-
спечение, теперь можно самостоятельно решать, что должен выполнять
компьютер.
Старт программы
14
Введение в программирование
ющей. При старте процесса это будет адрес первой команды программы.
Каждая команда указывает процессору прочитать, записать или вычис-
лить данные. Процессор может сравнивать данные, хранящиеся в ячей-
ках памяти, и перемещать их в зависимости от результата обработки.
Команды, интерпретируемые процессором, являются системными ко-
мандами.
15
Глава 1
16
Введение в программирование
1.1.2. Алгоритм
17
Глава 1
Тот, кто в первый раз создает эскиз алгоритма, понимает, как тяже-
ло описать метод работы так, чтобы при его выполнении получить тре-
буемый результат. Прервем немного изучение и выполним маленькое
упражнение: рассмотрим то, как мы считаем. Для лучшего самоконтро-
ля напишите на листе бумаги числа от 1 до 10.
18
Введение в программирование
Компилируемый язык
19
Глава 1
Происхождение языка C
20
Введение в программирование
21
Глава 1
22
Введение в программирование
23
Глава 1
int main()
{
}
Листинг 1.1. Самая короткая программа
1.2.1. Комментарии
Если у нас уже есть программа, которая ничего не делает, нужно обя-
зательно добавить в нее команды, которые тоже ничего не делают.
24
Введение в программирование
int main()
{
//Здесь начинается комментарий
//Следующая строка
//Ваши собственные комментарии
}
Листинг 1.2. Построчные комментарии
int main()
{
/*Здесь начинается комментарий
Следующая строка комментария не нуждается
в собственных символах комментария
*/
}
Листинг 1.3. Блочный комментарий
25
Глава 1
int main()
{
/* Здесь начинается комментарий
/* Следующие строки не требуют
знака комментария
*/
Это компилятор попробует перевести
*/
}
Листинг 1.4. Это некорректное написание!
26
Введение в программирование
1.2.2. Команды
1.2.3. Блоки
int main()
{
Здесь находятся команды;
Это тоже относится сюда;
{
Новый блок, заключенный в фигурные скобки;
Мы остаемся на этом уровне;
27
Глава 1
1.3. Переменные
Раздел, касающийся переменных, содержит в себе много деталей,
которые сейчас следует опустить. Начинающему программисту, кото-
рый будет читать этот раздел, не нужно запоминать всю информацию.
Сначала нужно получить основное представление о переменных, типах
и константах. К этому разделу можно вернуться в любой момент, если
захочется заполнить пробелы в знаниях.
28
Введение в программирование
int main()
{
int income;
}
Листинг 1.6. Объявление переменной
29
Глава 1
int income=0;
int i, j=0, k;
int i;
int j=0;
int k;
30
Введение в программирование
{
int a = 5;
{
// здесь а равно пяти.
}
{
int a = 3;
// здесь а присваивается значение 3.
{
// а все еще равно 3
}
}
//а здесь а опять равно 5
}
Листинг 1.7. Две переменные
31
Глава 1
32
Введение в программирование
33
Глава 1
34
Введение в программирование
Хранение информации
Целые числа
int counter;
35
Глава 1
Есть два особенных случая для всех чисел: short и long. Оба атри-
бута могут быть расположены перед типом int. Имя атрибута указывает
на максимальное число, которое может хранить переменная, и соответ-
ственно на количество памяти, которое выделяется под эту переменную.
Переменные типа short или long также могут быть объявлены в каче-
стве беззнаковых. При этом перед ними указывается ключевое слово
unsigned.
1
Бьерн Страуструп. Язык программирования С++. М.: Бином, 2011.
36
Введение в программирование
~ 0000
- 0001
Перенос: 111
----
Результат: 1111
Листинг 1.9. Бинарный расчет 0-1
~ 1111
- 0001
Перенос: 111
----
Результат: 0000
Листинг 1.10. Бинарный расчет –1+1
Легко заметить, что знак числа находится в первом бите. Здесь ука-
зана единица, которая обозначает минус.
ВНИМАНИЕ
Если первый бит бинарного кода числа единица, то это отрицатель-
ное число.
~ 1101
- 0101
Перенос: 1 1
----
Результат: 0010
Листинг 1.11. Бинарный расчет –3+5
ПРИМЕЧАНИЕ
Есть нечто коварное в том факте, что вы не можете преодолеть пре-
дел максимального числа целого типа. Если увеличить его на 1, то
снова получается самое маленькое значение. Таким удивительным
образом будет работать программа. Однако может оказаться, что
разработчик этого не заметит. Его задача — следить за тем, чтобы
такого перехода не произошло.
Символы
40
Введение в программирование
41
Глава 1
42
Введение в программирование
43
Глава 1
числа с плавающей запятой. Это значит, что будет выделено столько па-
мяти, сколько необходимо для представления числа.
Язык C++ не устанавливает четкие требования к количеству памяти
для большинства типов переменных. Такие детали реализации зависят
от компилятора. Четко установлены лишь качественные различия меж-
ду типами. Можно положиться на то, что short не больше long. Табл. 1.4
демонстрирует, какой размерный порядок существует на практике.
44
Введение в программирование
Константы Значение
INT_MAX Максимальное значение переменной int
INT_MIN Минимальное значение переменной int
UINT_MAX Максимальное значение переменной unsigned int
CHAR_MAX Максимальное значение переменной char
CHAR_MIN Минимальное значение переменной char
WCHAR_MAX Максимальное значение переменной wchar_t
WCHAR_MIN Минимальное значение переменной wchar_t
UCHAR_MAX Максимальное значение переменной unsigned char
SHRT_MAX Максимальное значение переменной short
SHRT_MIN Минимальное значение переменной short
USHRT_MAX Максимальное значение переменной unsigned short
LONG_MAX Максимальное значение переменной long
LONG_MIN Минимальное значение переменной long
ULONG_MAX Максимальное значение переменной unsigned long
1 За счет такой гибкости sizeof() занимает особое место среди функций. Посколь-
ку почти для всех стандартных функций передаваемыми параметрами могут служить
только значения или переменные, чей тип проверен компилятором.
2 Ходит слух, что секретные службы во время холодной войны требовали такие уди-
вительные вещи на случай захвата врагом, чтобы он не смог обращаться с компьютером.
45
Глава 1
int
short
long
char
unsigned wchar_t
float
double
long
Известно, что существуют типы char, wchar_t, int, float или double.
Для типа int или char можно использовать атрибут unsigned. Перед
46
Введение в программирование
1.3.6. Константы
Целые числа
ПРИМЕЧАНИЕ
Десятичная целочисленная константа — это 0 или другое число, на-
чинающееся с цифры, отличной от 0, за которой могут следовать
любые цифры, включая 0.
DecConst
0
2 0
3 ...
... 9
9
Рис. 1.6. Граф десятичной целочисленной константы DecConst
DecConst
Константа Причина
1 234 Указан пробел между 1 и 2
1,234 Запятая в константах не используется
1- Знак числа должен быть указан перед числом
12DM Содержимое недопустимо
ПРАВИЛО
Каждая целочисленная константа, которая начинается с нуля, не бу-
дет интерпретирована в качестве десятичной. Нужно избегать ука-
зывать 0 перед константами, если разработчик не знает точно, как
выполняется их интерпретация.
Если перед константой указан 0, а за ним буква «x», значит это шест-
надцатеричная константа, то есть число с базисом 16, которое состоит из
цифр от 0 до 9 и букв от «A» до «F», используемых для чисел от 10 до 15.
48
Введение в программирование
ПРИМЕЧАНИЕ
Шестнадцатеричная целочисленная константа начинается с после-
довательности символов 0х или 0Х. Затем следуют любые цифры от
0 до 9 или буквы от «A» до «F» (равнозначно от «a» до «f»).
49
Глава 1
0 x 0
X ...
...
...
ПРИМЕЧАНИЕ
Восьмеричная целочисленная константа всегда начинается с 0. За-
тем следуют любые другие цифры от 0 до 7.
0 0
...
...
50
Введение в программирование
Данный граф три раза ссылается на DecConst. Здесь речь идет о де-
сятичной константе, как показано в графе на рис. 1.6.
51
Глава 1
Символьные константы
dec 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
hex 0 1 2 3 4 5 6 7 8 9 A B C D E F
00 0 ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O
16 10 ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z
32 20 ! " # $ % & ' ( ) * + , - . /
48 30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
64 40 @ A B C D E F G H I J K L M N O
80 50 P Q R S T U V W X Y Z [ \ ] ^ _
96 60 ` a b c d e f g h i j k l m n o
112 70 p q r s t u v w x y z { | } ~
52
Введение в программирование
53
Глава 1
Последовательность Значение
\n Перевод строки (line feed)
\r Возврат каретки (carriage return)
\t Табуляция
\b Возврат назад
\f Перевод формата
\0 Действительный ноль (не знак '0')
\\ Вывод символа обратного слеша
\" Вывод двойной кавычки
\' Вывод одинарной кавычки
\0nnn Восьмеричное число nnn определяет символ
\0xnn Шестнадцатеричное число nn определяет символ
Последовательности символов
Последовательности символов состоят из нескольких символов,
связанных друг с другом (рис. 1.12). Все, что должно принадлежать
константной последовательности символов, заключается в кавычки.
В одной последовательности могут использоваться все символы, кото-
рые может содержать символьная константа, а также символы управле-
ния. Например, следующая последовательность состоит из двух строк:
"Это последовательность символов \n из двух строк"
Иногда код может быть очень длинным и не помещаться в одну
строку. Поэтому несколько символьных последовательностей, следую-
щих друг за другом, можно разделить на несколько строк.
54
Введение в программирование
Символические константы
55
Глава 1
1.4. Обработка
Итак, существуют переменные и константы. В этом разделе будет опи-
сано, как константы превращаются в переменные, как можно копировать
и вычислять переменные. В общем, как вдохнуть в компьютер жизнь.
1 Отличия понятий «объявление» и «определение» см. в глоссарии на стр. 521.
56
Введение в программирование
1.4.1. Присваивание
MwStSet = 16;
Netto = 200;
MwStSum = Netto * MwStSet / 100;
Brutto = Netto + MwStSum;
Листинг 1.12. Присваивание
a= b = c = d = 5 + 2;
57
Глава 1
58
Введение в программирование
1.4.3. Сокращения
counter = counter + 1;
Листинг 1.13. Содержимое переменной counter увеличивается на 1
counter +=1;
Листинг 1.14. Сокращенная запись инкрементирования
59
Глава 1
counter++;
Листинг 1.15. Сверхкороткая запись
counter = 5;
Sum = 2 + counter ++; // Sum содержит 7!
Листинг 1.16. Инкремент с правой стороны
60
Введение в программирование
Верно предположить, что оно будет равно 7. Как сказано выше, уве-
личение переменной counter на 1 произойдет только после вычисле-
ния арифметического выражения. Можно также указать двойной плюс
перед переменной. Тогда ее значение сначала увеличится на 1, а затем
будет использовано для вычисления выражения.
counter = 5;
Sum = 2 + ++ counter; // Sum содержит 8!
Листинг 1.17. Другой результат
counter = 5;
++ counter;
Sum = 2 + counter;
Листинг 1.18. Аналогичная запись, но более легкая для прочтения
f = sin(45);
63
Глава 1
cube= rand() % 6 + 1;
int value;
value = (int)something; // Классический C-кастинг
value = int(something); // C++
64
Введение в программирование
Наконец стоит сказать, что скобки вокруг 4/3 с точки зрения языка
C++ совсем не лишние, ведь выражение содержит операции одинакового
приоритета, и следовательно, будет вычисляться слева направо. Поэто-
му скобки имеют далеко не формальное значение. Тот, кто впоследствии
станет искать ошибку в программе, сразу увидит, как автор хотел рас-
ставить приоритеты. Если в каком-то выражении возникают сомнения
в приоритете вычислений, лучше указать больше скобок. Программа от
этого не станет выполняться медленнее. Компилятор все равно уберет
такие скобки при оптимизации.
1 «Out» в команде cout переводится с английского языка как «наружу». Это направ-
ленный объект вывода. Буква «с» стоит впереди потому, что это любимая буква Бьерна
Страуструпа. Поэтому он и назвал свой язык C++.
66
Введение в программирование
67
Глава 1
68
Введение в программирование
}
Налить в кофеварку 1 литр воды
Включить кофеварку
Повторить:
{
Подождать минуту
} до тех пор, пока кофе не перестанет капать из фильтра
Выключить кофеварку
Забрать напиток
2.1. Ветвление
Есть различные причины, из-за которых программа, в зависимости
от значения определенных переменных, не должна выполнять те или
иные операции. Приведу несколько типичных примеров:
• Совершенно очевидно, что программа не должна производить деле-
ние, когда знаменатель равен 0.
• Перед тем, как извлечь корень, стоит проверить, не является ли чис-
ло отрицательным. Программе не следует стрелять в инопланетян,
если последняя ракета уже выпущена из космического корабля.
2.1.1. По условию: if
71
Глава 2
if (denominator != 0)
result = numerator / denominator;
Листинг 2.1. Проверка
После команды if может стоять еще одно условие. Оно будет про-
верено только в том случае, если выполняется первое. Такие последова-
72
Циклическое программное управление
c=2;
if (a==0)
if (b==0)
c=0;
cout << c << endl;
Листинг 2.2. Вложения оператора if
if (denominator != 0)
result = numerator / denominator;
if (denominator == 0)
cout << "Делитель равен 0! Расчет невозможен!";
Листинг 2.3. Проверка двух противоположных условий
if (denominator != 0)
result = numerator / denominator;
else
cout << "Делитель равен 0! Расчет невозможен!";
Листинг 2.4. Упрощенная проверка противоположного условия
Структурные диаграммы
74
Циклическое программное управление
75
Глава 2
if (year<3)
{
percent=3;
}
else
{
if (year<6)
{
percent=4;
}
else
{
percent=5;
}
}
Листинг 2.5. Вложенное условие if
«Висящий» else
if (a == 0)
if (b == 0)
c=5;
else
cout << "К чему я отношусь?";
Листинг 2.6. «Висящий» else
76
Циклическое программное управление
случае относит ключевое слово else к последней команде if, таким об-
разом, оно принадлежит к проверке переменной b.
if (a == 0) if (a == 0)
{ {
if (b == 0) if (b == 0)
{ {
c=5; c=5;
} }
} else
else cout << "Куда?";
cout << "Куда?"; }
Листинг 2.7. Фигурные скобки однозначно указывают принадлежность
77
Глава 2
if (level == 1)
{
cout << "Сладости, книги" << endl;
}
else if (level == 2)
{
cout << "Одежда" << endl;
}
else if (level == 3)
{
cout << "Одежда" << endl;
}
else if (level == 4)
{
cout << "Игрушки" << endl;
}
else if (level == 5)
{
cout << "Электроника " << endl;
}
else
{
cout << "Гараж" << endl;
}
Листинг 2.8. Каскадная проверка оператором if
78
Циклическое программное управление
ном конкретном случае листинг не стал запутаннее от того, что эти от-
ступы я опустил. Такую форму можно часто встретить в листингах, если
команда if следует сразу же за командой else.
switch (level)
{
case 1:
cout << "Сладости, книги" << endl;
break;
case 2:
case 3:
cout << "Одежда" << endl;
break;
case 4:
cout << "Игрушки" << endl;
break;
case 5:
cout << "Электроника" << endl;
break;
default:
cout << "Гараж" << endl;
break;
}
Листинг 2.9. Оператор case
79
Глава 2
80
Циклическое программное управление
switch (symbol)
{
case '3':
case '4':
case '9':
a = 4;
case '5':
d = 2;
break;
case '6':
e = 1;
break;
}
Листинг 2.10. Оператор выбора
Если значение переменной symbol равно '3', '4' или '9', тогда сначала
переменной a будет присвоено значение 4. Поскольку затем нет коман-
81
Глава 2
if (value >= 0)
{
Abs = value;
}
else
{
Abs = - value;
}
Листинг 2.11. Расчет абсолютной величины с помощью команды if
82
Циклическое программное управление
Minimum = a < b ? a : b;
83
Глава 2
2.2.2. Операторы
if (a < b)
84
Циклическое программное управление
if (a <= b)
if (a == b)
85
Глава 2
if (a==1)
{
a=5;
}
Листинг 2.15. Исправлено!
Оператор Значение
a == b a равно b ?
a != b a неравно b?
a > b a больше b?
a >= b a больше или равно b?
a < b a меньше b?
a <= b a меньше или равно b?
86
Циклическое программное управление
if (5<=a<=10) // не работает!
{
// сделай что-нибудь;
}
Листинг 2.16. Еще один коварный случай!
if (5<=a)
{
if (a<=10)
{
// сделай что-нибудь;
}
}
Листинг 2.17. Решение с помощью вложения
1
Для экспертов: операция сравнения является операцией с левой привязкой.
87
Глава 2
Если значение меньше, чем минимум, значит, оно уже лежит вне ин-
тервала. Объединение, которое приводит к результату true, если одно
из объединенных условий истинно, называется «ИЛИ»-объединением
и обозначается с помощью символов ||1.
88
Циклическое программное управление
if (5>a || a>10)
{
// сделай что-нибудь;
}
Листинг 2.19. Объединение «ИЛИ»
89
Глава 2
90
Циклическое программное управление
Закон Де Моргана
if (!(a>=2 %% a<=5))
91
Глава 2
if (!(a>=2) || (a<=5))
Короткое замыкание
92
Циклическое программное управление
personal_nr++;
if ((personal_nr>10) && (salary<20000))
{
...
Листинг 2.22. Конец головоломкам
93
Глава 2
if (salary<20000)
{
if (personal_nr++>10))
{
...
Листинг 2.23. Идентично, но более наглядно
94
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
i = 1; // Установлено начальное значение переменной
while (i <= 10) // Условие цикла
{
cout << i << endl; // Вывод
i++; // Без этой строки цикл будет выполняться //бесконечно
}
}
Листинг 2.24. Счет
#include <iostream>
using namespace std;
int main()
{
i = 0;
while (i < 10)
{
i++;
cout << i << endl;
}
}
Листинг 2.25. Счет, вариант №2
96
Циклическое программное управление
i = 10;
while (i > 0)
{
i--;
}
Листинг 2.26. Обратный счет
97
Глава 2
98
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
int i = 10;
do
{
cout << i << endl;
i--;
}
while(i>0);
}
Листинг 2.27. Цикл do-while
Стартовая команда;
while(условие)
{
Тело цикла;
Завершающая команда;
}
100
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
int i, a;
for (i=0, a=10; i<5; i++, a--)
{
cout << i << "-" << a << endl;
}
}
Листинг 2.29. Несколько команд в управляющей части цикла for
101
Глава 2
error=0;
while (a>0)
{
...
c=4;
102
Циклическое программное управление
if (error==9)
{
break;
}
a++;
...
}
Листинг 2.30. Цикл прерывается командой break
error=0;
while (error!=9 && a>0)
{
...
c=4;
if (error!=9)
{
a++;
...
}
}
Листинг 2.31. Альтернатива оператору break
103
Глава 2
while (a>0)
{
...
c=4;
if (error==9)
{
continue;
}
a++;
...
}
Листинг 2.32. Команда continue пропускает оставшуюся часть тела цикла
while (a>0)
{
...
c=4;
104
Циклическое программное управление
if (error!=9)
{
a++;
...
}
}
Листинг 2.33. Альтернатива команде continue
if (a==0)
{
a++;
goto middle;
}
b+=2;
if (b>6)
{
goto end;
middle:
// здесь b>6 или a==1
b=4;
}
else
{
c=2;
}
end:
a++;
Листинг 2.34. Дикие скачки
105
Глава 2
2.4. Примеры
Циклы и условия — это основные блоки каждой программы. Нуж-
но потренироваться, чтобы в постановке задачи распознать условия
и циклы и научиться тому, как можно их комбинировать. С этой целью
в данной главе будут рассмотрены некоторые примеры. Они подобраны
таким образом, чтобы продемонстрировать, как циклы и условия можно
компоновать друг с другом. Диаграмма Насси-Шнейдермана исполь-
зуется здесь не только для наглядности, но и в качестве дизайнерского
инструмента.
106
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
const int max_prime_number =100; // Конец расчета
int prime_number; // Тестовый кандидат
for (prime_number =3; prime_number <= max_prime_number;
prime_number ++)
// Проработать все числа
{
// Проверить, является ли число действительно //простым чис-
лом
// Тестовый вывод:
cout << prime_number << " ";
}
cout << endl;
}
Листинг 2.35. Простые числа с первым циклом
107
Глава 2
#include <iostream>
using namespace std;
int main()
{
const int max_prime_number =100;
int prime_number, divisor;
for (prime_number=3; prime_number<=max_prime_number;
prime_number++)
{
// Проверка, является ли простое число действительно //про-
стым
for (divisor=2; divisor<=prime_number-1; divisor++)
{
// Тестовый вывод
cout << divisor << " ";
}
cout << endl;
}
cout << endl;
}
Листинг 2.36. Простые числа с внутренним циклом
#include <iostream>
using namespace std;
int main()
{
const int max_prime_number=100;
108
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
const int max_prime_number=100;
int prime_number, divisor;
bool the_prime_number;
cout << "2";
109
Глава 2
110
Циклическое программное управление
#include <iostream>
using namespace std;
int main()
{
const int max_prime_number=100;
int prime_number, divisor;
bool the_prime_number;
cout << "2";
for (prime_number=3; prime_number<=max_prime_number;
prime_number++)
{
the_prime_number = true;
// Проверка, является ли простое число действительно //про-
стым
for (divisor=2; the_prime_number &&
divisor < prime_number; divisor++)
{
// Есть ли остаток от деления?
if (0==prime_number % divisor)
{
// Число можно разделить, значит оно не //простое!
the_prime_number = false;
}
}
// Проверка окончена.
// Если это простое число, выводим его!
if (the_prime_number)
{
cout << ", " << prime_number;
}
}
cout << endl;
}
Листинг 2.39. Оптимизированный расчет простых чисел
111
Глава 2
Оба числа, для которых нужно найти НОД, назовем a и b. Идея осно-
вывается на том утверждении, что НОД — делитель не только для a и b, но
112
Циклическое программное управление
также для b и (a-b), пока b больше, чем a1. Значит, можно рассматривать
вместо a и b равнозначно b и (a-b) и уже не нужно искать, какое из этих
чисел больше. Так же можно поступить с двумя другими числами, не толь-
ко с a и b. Снова вместо большего числа будет использована разница между
ними. При дальнейшем повторении этого способа, когда самое маленькое
число станет равно 0, второе число при этом будет НОД (рис. 2.17).
help = a;
a = b;
b = help;
Листинг 2.40. Обмен значений переменных
1
Я предполагаю, что вам не интересно доказательство данного утверждения.
113
Глава 2
#include <iostream>
using namespace std;
int main()
{
int a, b, help;
cin >> a;
cin >> b;
while (b>0)
{
if (b>a)
{
// обмен a и b
help = a;
a = b;
b = help;
}
114
Циклическое программное управление
a = a - b;
}
cout << a << endl;
}
Листинг 2.41. Программа на языке C++ для нахождения НОД
2.5. Упражнения
• Дополните программу netto.cpp (см. стр. 497) из последней главы,
чтобы она проверяла, больше ли 0 цена за массу нетто товара, и толь-
ко тогда проводила расчет.
• Добавьте в программу netto.cpp сообщение об ошибке, которое будет
выводиться, если пользователь ввел некорректные данные. Пример
решения приведен на стр. 497.
• Сложные проценты: вы ежегодно вносите на счет под 5% годовых по
5000 Евро. Создайте таблицу с годовым взносом слева и состоянием
счета справа. Пример решения на стр. 498.
• Угадывание чисел: пусть компьютер выберет случайное число в диа-
пазоне от 1 до 1000. Пользователь вводит число и компьютер сооб-
щает, больше, меньше или равно это число тому, которое он выбрал.
Это будет повторяться до тех пор, пока пользователь не угадает чис-
ло. Пример решения приведен на стр. 498.
Глава 3
ТИПЫ ДАННЫХ И СТРУКТУР
Час управления типами данных:
в этой главе будут собраны воедино
все базовые типы, а из них созданы новые.
3.1. Массив
Массив1 — это комбинация нескольких переменных одного типа.
К элементам массива обращаются по их позиционному номеру.
1 5 13 27 43 44
116
Типы данных и структур
int lotto[6];
srand(0);
for(i=0; i<6; i++)
{
lotto[i] = rand() % 49 + 1;
}
Листинг 3.1. Числа лото в виде массива
117
Глава 3
int lotto[6];
int i, j;
bool new_number;
srand(0);
for(i=0; i<6; i++) // вытащим одно за другим шесть чисел
{
do // повторим вытаскивание числа до нового значения
{ // число отличается от всех предыдущих
lotto[i] = rand() % 49 + 1;
new_number = true; // удовлетворительный набор
for (j=0; j<i; j++)
{ // просмотреть все, ранее вытащенные шарики
if (lotto[j]==lotto[i])
{ // здесь обнаружено дублирование значения
new_number = false;
}
}
} while (!new_number);
}
Листинг 3.2. Числа лото в виде массива (lotto.cpp)
118
Типы данных и структур
int lotto[6];
lotto[0] = 1; // первый элемент!
...
lotto[5] = 3; // шестой элемент, ok!
lotto[6] = 4; // седьмой элемент: ошибка!!!
Листинг 3.3. Переход границы
119
Глава 3
Уже в начале главы цикл for был использован для работы с масси-
вом. Цикл for и массивы «принадлежат» друг другу. Чтобы в таком цикле
перебрать все элементы массива, нужно контролировать только некото-
рые моменты. Прежде всего, переменная индекса должна начинаться с 0.
Условие следует формулировать как «значение индексной переменной
меньше размера массива». В заключительной команде значение индекс-
ной переменной просто увеличивается. Особенно четко это видно, если
размер массива задан некоторой константой, в данном случае MAXLOTTO.
120
Типы данных и структур
#include <iostream>
using namespace std;
int main()
{
double c[13];
cout << "Количество памяти, занимаемое массивом double
c[13]: ";
cout << sizeof(c) << endl;
cout << " Количество памяти, занимаемое элементом: ";
cout << sizeof(c[0]) << endl;
cout << "Количество элементов: ";
cout << sizeof(c) / sizeof(c[0]) << endl;
}
Листинг 3.7. Функция sizeof() в массиве (sizeof.cpp)
121
Глава 3
протекает процесс, который приведет к тому, что в конце концов все кар-
ты будут лежать по порядку. При этом нужно учитывать, что компьютер
может сравнивать между собой одновременно только две переменные.
4 9 3 2
4 9
3 9
4 3 2 9
3 4
3 2 4 9
2 3 4 9
Рис. 3.2. Сортировка методом «пузырька»
122
Типы данных и структур
#include <iostream>
using namespace std;
#include <stdlib.h>
const int MAX=5;
123
Глава 3
int main()
{
int field[MAX], dop, help;
int i, j, k;
srand(56); // Генератор случайных чисел готов
for (i=0; i<MAX; i++)
{ // Заполнение и вывод массива
field[i] = rand() % 100 + 1;
cout << field[i] << " ";
}
cout << endl;
for(i=MAX-1; i>0; i--)
{
for (j=0; j<i; j++)
{
cout << "(" << j << "-" << j+1 << "): " ;
if (field[j]>field[j+1])
{ // Требуется перестановка
help = field[j];
field[j] = field[j+1];
field[j+1] = help;
}
cout << field[j] << " - " << field[j+1] << " ";
}
// Здесь выводится массив
cout << endl << MAX-i << "-й проход цикла завершен: ";
for (k=0; k<MAX; k++)
{
cout << field[k] << " ";
}
cout << endl;
}
}
Листинг 3.9. Сортировка методом «пузырька» (bubble.cpp)
124
Типы данных и структур
80 91 14 78 17
(0-1): 80 - 91 (1-2): 14 - 91 (2-3): 78 - 91 (3-4): 17 - 91
1-й проход цикла завершен: 80 14 78 17 91
(0-1): 14 - 80 (1-2): 78 - 80 (2-3): 17 - 80
2-й проход цикла завершен: 14 78 17 80 91
(0-1): 14 - 78 (1-2): 17 - 78
3-й проход цикла завершен: 14 17 78 80 91
(0-1): 14 - 17
4-й проход цикла завершен: 14 17 78 80 91
Упражнения
• Дополните программу лото сортировкой методом пузырька так, что-
бы числа лото всегда выводились по возрастанию. Пример приведен
на стр. 499.
• Программа сортировки методом «пузырька» может быть оптимизи-
рована. Если после одного из проходов цикла слева направо ни одно
число не было перемещено, тогда очевидно, что числа уже располо-
жены в правильном порядке, и нет необходимости в их дальнейшей
сортировке. Пример на стр. 501.
1
Массив так же не может быть возвращаемым значением для функции (см. стр. 128).
125
Глава 3
int source[MAX];
int target[MAX];
target = source; // нестандартно!
int source[MAX];
int target[MAX];
int i;
for (i=0; i<MAX; i++)
{
target[i] = source[i];
}
#include <string.h>
int source[MAX];
int target[MAX];
memcpy(target, source, sizeof(source));
126
Типы данных и структур
127
Глава 3
но. Там могут находиться еще старые данные, обозначенные на рис. 3.4
символами 'z' и 'M'.
128
Типы данных и структур
#include <iostream>
using namespace std;
const int MAX=256;
int main()
{
char input[MAX];
int i = 0;
long value = 0;
cin.getline(input, MAX);
while (input[i]>='0' && input[i]<='9')
{
value *= 10;
value += input[i] — '0';
i++;
}
cout << value << endl;
}
Листинг 3.10. Считывание целых значений
cin.getline(input, MAX);
129
Глава 3
#include <iostream>
using namespace std;
const int MAX=256;
int main()
{
char input[MAX];
int i=0;
double value = 0;
130
Типы данных и структур
cin.getline(input, MAX);
while (input[i]>='0' && input[i]<='9')
{
value *= 10;
value += input[i] — '0';
i++;
}
if (input[i]==',')
{
double NK = 1;
i++;
while (input[i]>= '0' && input[i]<= '9')
{
NK *= 10;
value += (input[i]- '0')/NK;
i++;
}
}
cout << input << value << endl;
}
Листинг 3.11. Считывание чисел с запятой (number_input.cpp)
Упражнение
• Напишите программу, которая позволяла бы вводить дроби и кон-
вертировала их в переменные типа long. Числитель и знаменатель
должны разделяться слешем. При вводе 3/4 переменная должна со-
держать 0.75. Пример приведен на стр. 502.
131
Глава 3
#include <iostream>
using namespace std;
const int X=9;
const int Y=7;
int main()
{
char game_space[X][Y];
int x, y;
char cx, cy;
for (x=0; x<X; x++)
{
for (y=0; y<Y; y++)
{
game_space[x][y] = '.';
}
}
// Создание игрового поля и ввод координат
bool loop_end=false;
int xin, yin;
do
{
cout << " 1 2 3 4 5 6 7 8 9" << endl;
for (y=0; y<Y; y++)
{
cout << (char)('A'+y) << " ";
for (x=0; x<X; x++)
{
cout << " " << game_space[x][y];
}
cout << endl;
}
133
Глава 3
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
C . . . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . . . . .
G . . . . . . . . .
2C
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
134
Типы данных и структур
C . x . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . . . . .
G . . . . . . . . .
6F
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
C . x . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . x . . .
G . . . . . . . . .
Xx
135
Глава 3
136
Типы данных и структур
char *char_pointer;
137
Глава 3
138
Типы данных и структур
int main()
{
int *int_pointer = 0;
int intVar = 5;
int_pointer = &intVar;
// указатель получает адрес переменной intVar
// теперь int_pointer указывает на intVar
139
Глава 3
*int_pointer = 1;
// переменной, на которую указывает int_pointer, будет
// присвоено значение 1. При этом только содержимое
// intVar равно 1.
intVar = *int_pointer + 1;
// значение intVar будет изменено на сумму 1 и значение, на
// которое указывает int_pointer. Но только сама переменная.
// Поэтому значение intVar в итоге будет равно 2.
}
140
Типы данных и структур
values_pointer = values;
values_pointer[3] = 4;
int values[4];
int *values_pointer = 0;
values_pointer = values;
values_pointer[3] = 5;
values_pointer = & values[2];
values_pointer[1] = 3; // записывается в values[3]!
Листинг 3.13. Массив и указатель
141
Глава 3
int values[MAX];
for(i=0; i<MAX; i++)
{
values[i] = 0;
}
142
Типы данных и структур
int values[MAX];
int *pointer = values;
for(i=0; i<MAX; i++)
{
*pointer = 0;
pointer++;
}
char source[MAX];
for (char *p = source; *p; p++)
{
cout << *p ;
}
Листинг 3.14. Цикл for для строк
143
Глава 3
Сложение и вычитание
*(pointer+2) = 4;
pointer[2] = 4;
Листинг 3.15. Однозначные команды
144
Типы данных и структур
void *ptr;
145
Глава 3
void *voidPtr;
int *intPtr;
voidPtr = intPtr; // работает без проблем
intPtr = (int *)voidPtr; // необходимы скобки
146
Типы данных и структур
147
Глава 3
parking_map->price = 12500;
148
Типы данных и структур
float price;
} my_first_auto, my_dream_auto;
Листинг 3.18. Структура для автомобиля
В языке C++ для этой цели обычно используются классы (см. стр. 213
и далее), которые могут значительно больше, чем С-структуры. Однако
по причине совместимости ключевое слово struct используется в языке
C++ и, вероятно, будет и далее перениматься. Тем не менее, структура
в языке C++ позволяет значительно больше, чем в языке C. В целом она
соответствует классу, в котором все элементы доступны извне (рис. 3.9).
149
Глава 3
151
Глава 3
{
cout << Ship[i].x << "," << Ship[i].y << " ";
}
cout << endl;
}
Листинг 3.20. Прячем корабли (bermuda2.cpp)
152
Типы данных и структур
153
Глава 3
Если с помощью команды new был создан массив, этот участок памя-
ти освобождается специальной для массива записью команды delete[].
Хотя при нормальном вызове команды delete большинство компиля-
торов не найдут изъянов, результат будет непредсказуем.
154
Типы данных и структур
struct TList
{
int data;
TList *next;
};
155
Глава 3
#include <iostream>
using namespace std;
struct TList
{
int data; // симулирует данные
TList *next; // Связь со следующим элементом
};
TList *anchor = 0; // Начало списка
int main()
{
int content;
TList *knot, *old;
// Заполнение списка числами до тех пор, пока не встретится
//число 0
do
{
cout << "Введите число(0 для завершения ввода)"
<< endl;
cin >> content;
if (content)
{
// Новые элементы списка:
TList *knot = new TList;
knot->data = content; // Запись данных
knot->next = anchor; // Прикрепляем предыдущий //элемент
списка
anchor = knot; // Передаем дальше начальное //значение
}
} while (content);
// Выводим список в обратном порядке
// при этом удаляем выведенные элементы
while (anchor) // Не равен 0! Список не пустой!
{
cout << anchor ->data << endl;
156
Типы данных и структур
3.5. Объединение
Объединение — union — представляет собой особый тип структу-
ры. Его можно описать в качестве связи или-или. В одном объедине-
нии хранится несколько переменных, из которых можно использовать
только одну. То есть обрабатывать можно только один из представлен-
ных в объединении элементов. Объединение занимает столько памяти,
сколько требуется для хранения самого большого из его элементов.
157
Глава 3
union tTransform
{
struct
{
unsigned char high;
unsigned char low;
} byte;
unsigned short word;
} Transform;
int main()
{
Transform.word = 7656;
cout << (int) Transform.byte.high << endl;
cout << (int) Transform.byte.low << endl;
}
Листинг 3.23. Программа union.cpp
158
Типы данных и структур
color traffic_lights;
…
traffic_lights = red;
color traffic_lights;
int i;
…
i = red; // никаких проблем
traffic_lights = 3; // появится предупреждение
a = sin(alpha);
#include <iostream>
using namespace std;
int next_number=0; // В этой переменной хранится состояние
счета
int next()
// изменение глобальной переменной next_number
{
next_number++; // увеличение глобальной переменной
return next_number;
}
int main()
{
int n;
n = next();
cout << n << endl;
next(); // здесь n не изменяется!
cout << n << endl;
n = next();
cout << n << endl;
}
Листинг 4.1. Функция next (next.cpp)
162
Функции
163
Глава 4
• Имя функции
Имя начинается с латинской буквы или знака подчеркивания. За-
тем следуют латинские буквы, цифры и знаки подчеркивания в лю-
бом порядке. Имя соответствует графу синтаксиса, показанному на
рис. 1.2 на стр. 33.
• Параметры
Функция может как вообще не иметь параметров, так и иметь лю-
бое их количество, при этом они разделяются запятой1. Определе-
ние параметра соответствует определению переменной. Подробнее
параметры рассмотрены в следующей главе.
• Тело
Это последовательность команд, которые составляют содержимое
функции и выполняются после ее вызова.
void separator()
{
cout << "-------------------------" << endl;
return;
}
int main()
{
164
Функции
separator();
cout << "Программа для определения..." << endl;
separator();
}
Листинг 4.2. Простая функция separator()
За кадром
Как получается, что программа после вызова функции всегда на-
ходит правильный адрес возврата? Для этого используется так назы-
ваемый стек1 — особая структура памяти, куда могут записываться дан-
ные, которые будут считываться в обратном порядке. Такую структуру
можно представить в виде стопки книг. Если складывать книги одна на
другую, прочесть можно только верхнюю, и получить книги из стопки
можно только в порядке, обратном тому, как их складывали. Таким же
образом при вызове функции адрес команды записывается в стек. После
того как функция выполнена, программа возвращается по последнему
адресу в стеке. Неважно, сколько переходов из одной функции в другую
содержит программа, адрес возврата надежно защищен.
По информации, которая хранится в стеке, в случае сбоя программы
отладчик сделает вывод, после вызова какой функции программа дала
сбой. Возможность помещать часто используемые блоки кода в функ-
цию и вызывать ее в тех местах программы, где они используются, по-
зволяет исключить ошибки при копировании части кода из одного места
1
От англ. stack — стопка. Прим. ред.
165
Глава 4
4.1. Параметры
Функция может иметь параметры, которые служат для передачи
в нее значений. Это делает работу с ней значительно более гибкой. Что-
бы из функции добраться до значения переменной в основной програм-
ме, можно, конечно, использовать и глобальные переменные. Однако
в параметрах открыто указывается, какую информацию функция полу-
чает извне. При этом можно говорить об интерфейсе функции.
166
Функции
{
int width = 45;
separator(width);
cout << "Программа для определения..." << endl;
separator(45);
}
Листинг 4.3. Функция separator() с параметром
167
Глава 4
На рис. 4.2 повторно представлен граф с рис. 4.1. На рис. 4.3 он будет
дополнен параметрами функции.
168
Функции
4.1.1. Прототипы
169
Глава 4
170
Функции
Константный указатель
171
Глава 4
Если ключевое слово const указано перед типом, это означает, что
переменная основной программы не может изменяться с помощью тако-
го указателя. Указатель сам по себе может инкрементироваться в функ-
ции, например, для перебора элементов массива.
Если ключевое слово const стоит перед именем указателя, это озна-
чает, что указатель const_pointer не изменяется. Например, он не мо-
жет быть инкрементирован. При этом разрешено изменение перемен-
ной, на которую он ссылается.
172
Функции
Многомерный массив
#include <iostream>
using namespace std;
int func(int ¶meter)
{
parameter = parameter + 4;
}
int main()
{
int number = 5;
174
Функции
func(number);
cout << number << endl; // результат: 9!
}
Листинг 4.6. Передача параметров по ссылке (linkpar.cpp)
Упражнение
• Напишите функцию с именем swap(), которая получает два ссылоч-
ных параметра и обменивает их содержимое между собой. Решение
приведено на стр. 503.
175
Глава 4
#include <iostream>
using namespace std;
struct TListStack
{
int data; // симуляция данных
TListStack *next; // связь со следующим элементом
};
TListStack *Anchor = 0; // глобальный начальный пункт стека
void push(int data) // добавление нового элемента
{
// создание нового элемента:
TListStack *node = new TListStack;
node->data = data; // заполнение данными
node->next = Anchor; // прикрепление к предыдущему элементу
Anchor = node; // установка нового начального пункта
}
int pop() // получение последнего записанного в стек элемен-
та
{
int content=0; // промежуточное сохранение элемента
if (Anchor) // не равен 0! Стек не пустой!
{
// сохранение указателя чтобы в будущем удалить //элемент:
TListStack *old = Anchor;
1
В пер. с англ. «толкать». Прим. ред.
2
В пер. с англ. «вытягивать». Прим. ред.
176
Функции
Начиная со стр. 229, тема стека будет рассмотрена еще раз на базе
классов. С помощью классов можно значительно легче и надежнее реа-
лизовать связанные списки.
Упражнение
• Расширьте программу стека так, чтобы в ней можно было использо-
вать не один , а сколько угодно различных стеков, в том числе и ло-
кальных. Подсказка: вместо функций push() и pop() в глобальном
списке используйте переменную Anchor, список следует передавать
в качестве параметра. Решение приведено на стр. 504.
177
Глава 4
179
Глава 4
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
180
Функции
#include <iostream>
using namespace std;
#include <stdarg.h>
void func(int num, ... )
181
Глава 4
{
va_list param; // указатель на параметр
int intpar; // содержимое параметра
va_start(param, num); // подготовка доступа
// просмотр всех параметров
//(зависит от переменной num)
for (int i=0; i<num; i++)
{
intpar = va_arg(param, int); // достать параметр
cout << intpar << endl; // вывести его
}
cout << "---" << endl;
va_end(param); // закрыть доступ
}
int main()
{
// проверка функции с различными параметрами
func(2, 5, 2);
func(0);
func(4, 5, 2, 0, 3);
}
Листинг 4.10. Доступ к дополнительным параметрам (vararg.cpp)
182
Функции
#include <iostream>
using namespace std;
void show(int i)
{
cout << "int: " << i << endl;
}
void show(double f)
{
cout << "double: " << f << endl;
}
int main()
{
show(12); // вызывает первую функцию (12 тип int)
show(2.5); // вызывает вторую функцию (2.5 тип float)
}
Листинг 4.11. Перегруженные функции (overload.cpp)
183
Глава 4
#include <iostream>
using namespace std;
inline int min(int a, int b)
{
return a<b ? a : b;
}
int main()
{
cout << min(4, 3) << endl;
cout << min(3, 4) << endl;
}
Листинг 4.12. Встроенная функция (inline.cpp)
#include <iostream>
using namespace std;
int main()
184
Функции
{
cout << 4<3 ? 4 : 3 << endl;
cout << 3<4 ? 3 : 4 << endl;
}
Листинг 4.13. Преобразование
185
Глава 4
int main()
{
init_game_field():
do
{
output_game_field();
input_data();
search_ships();
write();
}
while (!end_game());
}
186
Функции
int main()
{
char game_field[X][Y];
tShip ship[max_ship];
int x, y;
int number;
init_game_field(game_field, ship);
do
{
output_game_field(game_field);
input_data();
number=search_ships(ship,x,y);
write(game_field, x, y, number);
}
while (!end_game(ship));
}
Листинг 4.14. Главная программа
187
Глава 4
188
Функции
189
Глава 4
return 1;
}
x++; y--;
}
return 0;
}
Листинг 4.17. Диагональный поиск
190
Функции
Упражнение
• Это конечно не очень хорошо, что поиск в различных направлени-
ях осуществлен разными, но, тем не менее, похожими друг на дру-
га функциями. Вместо этого значительно более элегантно было бы,
если бы использовалась только одна функция, которая выполняла
бы поиск в различных направлениях, в зависимости от того, какие
параметры в нее передаются. Для реализации этой задачи требует-
ся поразмыслить. Однако не сдавайтесь слишком быстро. Решение
приведено на стр. 506.
Функции должны быть написаны так, чтобы они были как можно
более закрытыми от доступа извне. Особенно это касается переменных.
191
Глава 4
192
Функции
193
Глава 4
#include <iostream>
using namespace std;
int globvar = 1;
void func()
{
static int statvar = 1;
int locvar = 1;
cout << "статическая:" << statvar;
cout << "глобальная:" << globvar;
cout << "локальная:" << locvar << endl;
statvar++;
globvar++;
locvar++;
}
int main()
{
func();
func();
func();
locvar = 8; // компилятор выдаст ошибку
statvar = 8; // компилятор выдаст ошибку
globvar = 8; // это работает
func();
func();
}
Листинг 4.20. Статическая переменная
194
Функции
195
Глава 4
196
Функции
За кадром
sum_recursion(4) [=6]+4
sum_recursion(3) [=3]+3
sum_recursion(2) [=1]+2
sum_recursion(1) [=0]+1
sum_recursion(0)
197
Глава 4
print_recursion(char * FileName)
{
open_file(FileName)
while (!end_file())
{
read_line();
if (command_include(IncludeFileName))
{
print_recursion(IncludeFileName);
}
print_line();
}
}
Листинг 4.22. Рекурсия печати
198
Функции
27
16 36
7 19 43
Еще раз следует отметить то, что с точки зрения биологии кажет-
ся странным: корни такого дерева находятся вверху. В примере выше
в корне находится число 27. Узлы слева содержат значения меньше 27,
справа — больше. Это верно для всех узлов этого дерева.
struct tTree
{
int content;
tTree *left;
tTree *right;
};
Листинг 4.23. Узел бинарного дерева
199
Глава 4
tTree *NewKnot;
NewKnot = new tTree;
NewKnot -> content = 5;
Листинг 4.24. Создание и заполнение узлов бинарного дерева
struct tTree
{
int content;
tTree *left;
tTree *right;
};
void output_tree(tTree *leaf)
{
if (leaf==0) return;
output_tree(leaf->left);
cout << leaf->content << endl;
output_tree(leaf->right);
}
Листинг 4.25. Вывод отсортированного бинарного дерева (bintree.cpp)
200
Функции
201
Глава 4
Упражнение
• Напишите функцию рекурсии, которая ищет числа в бинарном дере-
ве. Решение приведено на стр. 507.
Доказательство
1. Утверждение
Если башня состоит из одного кольца, можно просто переложить его
с левого колышка на правый.
202
Функции
2. Индуктивный шаг
Докажем, что башню из n+1 колец можно правильно переместить,
если можно это сделать с башней из n колец. Будем исходить из того, что
слева находится n+1 колец.
Доказано.
#include <iostream>
using namespace std;
void carry(int from, int to, int rings_number)
{
int free;
if (rings_number==0) return;
free = 6 — from - to; // определение свободного
//места
203
Глава 4
204
Функции
enum tToken
{
PLUS, MINUS, MUL, DIV, LPAR, RPAR, NUMBER, END, ERROR
};
// глобальные переменные:
tToken aktToken; // последняя распознанная лексема
double TokenNumber; // значение числовой константы
char *srcPos; // программная позиция
tToken searchToken()
// поиск с актуальной позиции следующей лексемы.
// здесь также устанавливаются числовые константы
// и записываются в глобальные переменные TokenNumber.
{
aktToken = ERROR;
if (*srcPos==0)
{
aktToken = END;
}
else
{
switch (*srcPos)
{
case '(': aktToken=LPAR; break;
case ')': aktToken=RPAR; break;
case '*': aktToken=MUL; break;
case '/': aktToken=DIV; break;
205
Глава 4
206
Функции
207
Глава 4
searchToken();
return Number;
case END:
return 1;
}
return Error("primary expected");
}
double MulDiv()
// определение деления и умножения
// рекурсивный вызов Brackets()!
{
double Number;
// вызов операции с наивысшим приоритетом
Number = Brackets();
while (aktToken==MUL || aktToken==DIV)
{
if (aktToken==MUL)
{
searchToken();
Number *= Brackets();
}
else if (aktToken==DIV)
{
searchToken();
Number /= Brackets();
}
}
return Number;
}
double PlusMinus()
// определяет сложение и вычитание
// рекурсивный вызов MulDiv()
{
double Number;
// вызов операции с наивысшим приоритетом
208
Функции
Number = MulDiv();
while (aktToken==PLUS || aktToken==MINUS)
{
if (aktToken==PLUS)
{
searchToken();
Number += MulDiv();
}
else if (aktToken==MINUS)
{
searchToken();
Number -= MulDiv();
}
}
return Number;
}
double analysis(char *s)
{
srcPos = s;
searchToken(); // предварительное определение первой
//лексемы
return PlusMinus();
}
int main(int argc, char* argv[])
{
double Number = analysis(argv[1]);
cout << Number << endl;
return 0;
}
Листинг 4.29. Синтаксический анализатор (calc.cpp)
Упражнения
• На стр. 372 приведен пример, как можно прочесть числа с плаваю-
щей запятой из символьной последовательности. Интегрируйте это
решение в код программы калькулятора, чтобы оно могла работать
также с числами типа double. Решение приведено на стр. 508.
• Дополните программу калькулятора так, чтобы между лексемами
мог располагаться пробел. При тестировании строку с пробелом
следует вводить в кавычках, поскольку она будет интерпретирована
в качестве нескольких параметров.
• Почему программа не путается со знаком минус, хотя иногда он мо-
жет быть знаком числа, а иногда обозначать арифметическую опера-
цию? Ответ на стр. 510.
1 Причина в том, что оболочка UNIX расценивает астериск как выделение места под
файл и можно получить забавные результаты.
210
Функции
са. Это не навредит, а для читателя будет понятно, что здесь речь идет об
адресе функции.
213
Глава 5
class tDate
{
public:
int Day;
int Month;
int Year;
};
Листинг 5.1. Класс даты — первый шаг
214
Классы
tDate today;
today.Day = 13;
Здесь создан объект today типа tDay. Объект содержит три цело-
численные переменные — Day, Month, Year, как это видно из объявления
класса. Чтобы получить доступ к переменной, следует сначала указать
имя объекта, затем точку, а после нее — имя элемента, к которому требу-
ется получить доступ. В примере дню присваивается значение 13.
tDate *once;
(*once).Day = 13;
once->Day = 13;
215
Глава 5
няшнее число или дату, которая будет через две недели. Короче говоря,
объединение данных не имеет особой ценности без функций, которые
их обрабатывают. Но и функции бесполезны без связи с данными. Так,
расчет сегодняшней даты имеет смысл только в связи с датой, а расчет
дней недели не может быть внедрен кроме как вместе с датой. По этой
причине функции так же интегрируются в классы, как и элементы дан-
ных. Функция, которая принадлежит к некоторому классу, называется
элементной функцией или от английского варианта — функцией-членом.
В других объектно ориентированных языках такие функции еще назы-
ваются методами или операциями.
class tDate
{
public:
int Day;
int Month;
int Year;
void NewYear(int Year);
};
today.NewYear(2000);
216
Классы
class tDate
{
public:
int Day;
int Month;
int Year;
void NewYear(int Year);
};
void tDate:: NewYear(int pYear)
{
Day = 1;
Month = 1;
Year = pYear;
}
Листинг 5.2. Класс с элементной функцией
class tDate
{
public:
217
Глава 5
int Day;
int Month;
int Year;
void NewYear(int Year);
{
Day = 1;
Month = 1;
Year = pYear;
}
};
Листинг 5.3. Класс с элементной функцией
Номенклатура
218
Классы
Также можно также получить доступ через имя класса. Для этого
указывается двойное двоеточие между ним и именем элемента.
219
Глава 5
class tDate
{
public:
220
Классы
tDate();
~tDate();
...
};
tDate::tDate()
{
Day=0; Month=0; Year=0;
}
tDate::~tDate()
{
}
Листинг 5.6. Конструктор и деструктор
{
tDate today;
tDate *tomorrow; // конструктор не вызывается!
tDate *holiday; // конструктор не вызывается
tomorrow = new tDate; // конструктор вызывается
holiday = new tDate[14]; // конструктор не вызывается 14 раз
delete tomorrow; // вызывается деструктор
...
delete[] holiday; // деструктор вызывается 14 раз
} // вызывается деструктор объекта today
Листинг 5.7. Вызов конструктора и деструктора
221
Глава 5
tDate::tDate() : Day(0),Month(0),Year(0)
{
}
Листинг 5.8. Альтернативная реализация
222
Классы
class tDate
{
public:
tDate(int Day, int Month, int Year=-1);
...
};
tDate::tDate(int Day, int Month, int Year)
{
this->Day=Day;
this->Month=Month;
this->Year=Year;
if (Year<0)
{
// установить текущий год
...
}
}
tDate Start(1,1,1970);
tDate NewYear(31,12);
tDate *Christmas = new tDate(24,12);
Листинг 5.9. Конструктор с параметрами
223
Глава 5
Конвертирующий конструктор
class tFraction
{
public:
tFraction(char *);
Add(tFraction&);
};
...
char input[MAXSTR];
getline(cin, input, MAXSTR);
tFraction b1(input);
getline(cin, input, MAXSTR);
b1.Add(input);
class tFraction
{
public:
224
Классы
explicit tFraction(long);
...
};
tFraction Fraction=12; // не будет работать через компилятор
tFraction Fraction(12); // так работает
Стандартный конструктор
225
Глава 5
class tFraction
{
public:
tFraction() {numerator=0; denominator=1;}
long get_denominator() {return denominator;}
long get_numerator() {return numerator;}
void set_denominator(long p)
{if (p!=0) denominator =p;}
void set_numerator(long p) {numerator=p;}
void Show();
private:
long numerator;
long denominator;
};
void tFraction::Show()
{
cout << numerator << "/" << denominator << endl;
226
Классы
}
int main()
{
tFraction fraction;
long in_denominator, in_numerator;
cout << "Введите через пробел числитель и знаменатель!"
<< endl;
cin >> in_numerator >> in_denominator;
fraction.set_denominator(in_denominator);
fraction.set_numerator(in_numerator);
fraction.Show();
}
Листинг 5.10. Расчет дроби с приватными данными
class tDate
{
private:
int day;
int month;
227
Глава 5
int year;
int weekday;
public:
tDate();
~tDate();
void set_day(int value);
void set_month(int value);
void set_year(int value);
int get_day() return day;
int get_month() return month;
int get_year() return year;
int get_weekday();
};
tDate::tDate()
{
weekday = -1;
}
void tDate::set_month(int value)
{
if (value>0 && value<13 && month!=value)
{
month = value;
weekday = -1;
}
}
Листинг 5.11. Класс с приватными элементами
При этом каждый, кто использует этот класс, учитывает, что при из-
менениях следует устанавливать значение переменной weekday равным
–1, а переменные day, month и year определять в качестве закрытых,
тем самым, защищая их от доступа извне. Их можно изменить только
с помощью элементных функций. Для этого используется функция
set_month(), которая, с одной стороны, не может присвоить месяцу
в объекте неверное значение. С другой стороны она заботится о том,
чтобы день недели обозначался как недействительный, как только из-
228
Классы
~tStack();
void push(int); // добавление информации
int pop(); // получение информации
private:
tNode * Anchor; // каждый стек имеет свой собственный
// якорь
};
tStack::tStack()
{
Anchor = 0; // подготовка пустого списка
}
// деструктор удаляет все лишние элементы
tStack::~tStack()
{
tNode *last = Anchor; // вспомогательный указатель для
//защиты якоря
while (Anchor) // пока в списке есть элементы
{
last = Anchor; // первый элемент защищен
Anchor = Anchor->next; // установлен якорь на
//следующий элемент
delete last; // первый элемент освобожден
}
}
// добавление нового элемента в начало списка
void tStack::push(int d)
{
tNode *node = new tNode; // создание элемента списка
node->d = d; // заполнения поля данных
node->next = Anchor; // прикрепление к списку
Anchor = node; // присвоение этого элемента якорю
}
// прочесть верхний элемент и удалить его из списка
int tStack::pop()
{
230
Классы
Упражнения
• Создайте класс tFIFO, который также создает динамическую струк-
туру данных. В отличие от стека, принцип FIFO1 аналогичен обслу-
живанию по очереди. Пакет, помещенный первым, также первым
и считывается. Подсказка для решения: не рекомендуется использо-
вать якорь, лучше заменить его на два указателя. Первый будет ссы-
латься на начало очереди, а второй — на конец. Решение приведено
на стр. 511.
1
Акроним от фразы First In, First Out — «первым пришел — первым ушел». Прим. ред.
231
Глава 5
5.3.3. Друзья
class pal;
class police
{
public:
void help(pal&);
};
// Чтобы класс pal мог использовать класс в интерфейсе,
класс
// colleague должен быть определен здесь
class colleague;
class pal
{
private:
int secret;
void spion();
friend void help(pal);
friend class colleague;
friend void police::help(pal&);
};
class colleague
{
232
Классы
public:
colleague()
{
JB.secret=007;
}
pal JB;
};
void help(pal Egon)
{
Egon.spion();
}
Листинг 5.13. Дружественные классы (friend.cpp)
233
Глава 5
Синтаксис
236
Классы
class tFraction
{
public:
tFraction() {numerator=0; denominator=1;}
long get_denominator() {return denominator;}
long get_numerator() {return numerator;}
void set_denominator(long p)
{if (p!=0) denominator =p;}
void set_numerator(long p) {numerator=p;}
void Add(long);
237
Глава 5
void Add(tFraction);
void Show();
private:
long numerator;
long denominator;
};
void tFraction::Add(long summ)
{
numerator+=summ*denominator;
}
void tFraction::Add(tFraction summ)
{
numerator = numerator*summ.get_denominator()
+ summ.get_numerator()*denominator;
denominator = denominator * summ.get_denominator();
}
Листинг 5.15. Расчет дробей с перегруженными функциями (fraction.cpp)
238
Классы
239
Глава 5
5.6.1. Сложение
#include <iostream>
using namespace std;
class tFraction
{
public:
tFraction() { numerator=0; denominator=1;}
long GetDenominator() {return denominator;}
long GetNumerator() {return numerator;}
void SetDenominator(long p) {if (p!=0) denominator=p;}
void SetNumerator(long p) {numerator=p;}
tFraction operator+(long);
1 Такой печальный опыт привел к тому, что в языке Java перегрузка операторов во-
обще отсутствует.
240
Классы
tFraction operator+(tFraction);
void Show();
private:
long numerator;
long denominator;
};
tFraction tFraction::operator+(long summand)
{
numerator+=summand*denominator;
return *this;
}
tFraction tFraction::operator+(tFraction summand)
{
numerator = numerator*summand.GetDenominator ()
+ summand.GetNumerator()*denominator;
denominator = denominator * summand.GetDenominator ();
return *this;
}
Листинг 5.16. Класс дробей с перегруженным оператором
241
Глава 5
class tDate
{
public:
tDate operator+(int Days);
};
tDate tDate::operator+(int Days)
{
// расчет даты
return *this;
}
Листинг 5.17. Перегрузка операторов
tDate today;
today = today + 14;
Листинг 5.18. Использование оператора
243
Глава 5
class tFraction
{
tFraction& operator++(); // префикс
tFraction operator++(int); // постфикс
...
};
tFraction& tFraction::operator++()
// префикс-инкремент
{
// расчет новой дроби
// также можно использовать: *this = *this + 1;
numerator += denominator;
return *this;
}
tFraction tFraction::operator++(int)
// постфикс-инкремент
{
tFraction oldFraction =*this; // сохранено старое значение
numerator+=denominator; // увеличение переменной
return oldFraction; // возврат старого значения
}
Листинг 5.19. Инкрементирование с префиксом и постфиксом (fraction.cpp)
244
Классы
#include <iostream>
using namespace std;
class tAuto
{
public:
tAuto() { sign=0; Reader=4; }
~tAuto() { delete sign; sign = 0; }
246
Классы
247
Глава 5
248
Классы
249
Глава 5
class tFraction
{
...
bool operator==(tFraction op2);
bool operator!=(tFraction op2)
{
return !(*this == op2);
}
...
};
Листинг 5.22. Неравенство
class tFraction
{
...
long GetDenominator() const {return denominator;}
long GetNumerator() const {return numerator;}
...
};
Листинг 5.24. Константные функции (fraction.cpp)
class tFraction
{
...
friend ostream& operator<<(ostream&, const tFraction&);
friend istream& operator>>(istream&, tFraction&);
...
};
ostream& operator<<(ostream& Stream, const tFraction& B)
{
return Stream << B.numerator << '/' << B.denominator;
}
Листинг 5.25. Константные функции (fraction.cpp)
#include <iostream>
using namespace std;
class tSafeString // класс строк с защищенным индексом
{
public:
tSafeString(int len) // максимальная длина строки
//должна быть установлена
{
maxLen = len;
252
Классы
253
Глава 5
1
Бьерн Страуструп. Язык программирования С++. М.: Бином, 2011.
254
Классы
class tCallMe
{
public:
int operator() (int)
{ /* сделать что-нибудь */ }
...
};
Листинг 5.27. Оператор вызова
255
Глава 5
5.7. Атрибуты
Атрибуты static и const уже были рассмотрены в связи с перемен-
ными. Применительно к классам они имеют несколько иное значение.
256
Классы
class StatClass
{
...
static int Numerator;
};
#include <iostream>
using namespace std;
class StatClass
{
257
Глава 5
public:
StatClass() {Numerator++;}
~StatClass() {Numerator--;}
static int HowMuch() {return Numerator;}
private:
static int Numerator;
};
int StatClass::Numerator=0;
int main()
{
cout << StatClass::HowMuch() << endl;
StatClass field[4];
cout << StatClass::HowMuch() << endl;
{
StatClass field[5];
cout << StatClass::HowMuch() << endl;
}
cout << StatClass::HowMuch() << endl;
}
Листинг 5.29. Статические переменные и функции в классах (statclass.cpp)
5.7.2. Константы
258
Классы
Константные функции
class tFraction
{
...
long GetDenominator () const {return denominator;}
long GetNumerator () const {return numerator;}
...
};
Листинг 5.31. Константные функции (fraction.cpp)
259
Глава 5
Эта переменная для одинаковой даты может иметь как значение дня
недели, так и значение –1, если день недели еще не рассчитан. При этом
дата объекта не изменится. Для обозначения таких переменных исполь-
зуется ключевое слово mutable. Этот элемент можно изменять с помо-
щью константных функций.
Инициализация констант
class tConstant
{
public:
const int MaxBuffer;
tConstant() : MaxBuffer(500) {}
tConstant(long size);
};
tConstant::tConstant(long size): MaxBuffer(size)
{
5.8. Наследование
Информатика полна аналогий. Так, в объектно ориентированном
программировании используется понятие «наследование», когда класс
является производным от другого класса и перенимает его свойства.
Один класс может служить базисом для создания другого без изме-
нения кода. Определяется новый класс, и указывается, что он является
производным от базового. Тогда все открытые элементы базового класса
будут принадлежать новому без повторного их определения. При этом
говорят, что новый класс наследует свойства базового класса.
261
Глава 5
262
Классы
class Person
{
public:
string Name, Address, Telephone;
};
class Partner : public Person
{
public:
string Kto, BLZ;
};
class employee : public Partner
{
public:
string health_insurance;
};
class customer: public Partner
{
public:
string deliver_address;
};
class supplier : public Partner
{
public:
tOpenPost *bills;
};
class out_duty : public employee
{
public:
tBlock Block;
};
Листинг 5.32. Личности
263
Глава 5
264
Классы
protected
class Basis
{
private:
int private;
protected:
int protect;
public:
int public;
};
class successor : public Basis
{
public:
void access()
265
Глава 5
{
a = private; // это приведет к ошибке!
a = protect; // это работает
a = public; // это работает в любом случае
}
};
int main()
{
Basis myVar;
a = myVar.private; // это, конечно же, не работает
a = myVar.protect; // это тоже
a = myVar.public; // это работает
}
Листинг 5.34. Ключевое слово protected
class Basis
{
private:
int private;
protected:
int protect;
public:
int public;
};
class successor : public Basis
{
protected:
using Basis::public;
public:
using Basis::protect;
};
266
Классы
int main()
{
successor a;
b = a.protect; // о чудо, это работает!
}
Листинг 5.35. Изменение типа доступа
class Basis
{
private:
int private;
protected:
int protect;
public:
int public;
};
class successor : Basis // осторожно: это private!
{
public:
267
Глава 5
protected
268
Классы
class tBasis
{
public:
int doSmth(int a);
};
class tSpecialCase : public tBasis
{
public:
int doSmth(int a);
};
int tSpecialCase:: doSmth(int a)
{
int oldValue = tBasis:: doSmth(a);
...
return oldValue;
}
Листинг 5.37. Вызов функции базового класса (kasfunc.cpp)
269
Глава 5
class tBasis
{
public:
tBasis(int i); // нет стандартного конструктора
};
class tSpecialCase : public tBasis
{
public:
tSpecialCase() : tBasis(5) // вызван базовый
//конструктор
{
...
}
};
Листинг 5.38. Вызов конструктора (kaskonstrukt.cpp)
270
Классы
Конструктор копирования
Оператор присваивания
271
Глава 5
272
Классы
#include <iostream>
using namespace std;
class Bass
{
public:
void boom() { cout << "Bass" << endl; }
};
class Tuba : public Bass
{
public:
void boom() { cout << "Tuba" << endl; }
};
int main()
{
Tuba tuba.boom();
Bass bass.boom();
}
Листинг 5.39. Пример — музыка
Нет ничего удивительного в том, что туба звучит иначе, чем бас.
И тем не менее, функция принадлежит этому объекту. Каждый объект
знает, какая из функций ему принадлежит. Но что делать, если нужно
собрать оркестр из этих инструментов и получить звук каждого из них?
273
Глава 5
нии, ведь тогда туба потеряет все свои особенности, которые отличают
ее от баса. Но если параметр является указателем, туба остается тубой
и может быть присвоена указателю типа бас. Если при передаваемом
указателе вызывается элементная функция boom(), нужно помнить, что
каждый объект вызывает принадлежащую ему функцию.
#include <iostream>
using namespace std;
class Bass
{
public:
virtual void boom() { cout << "Бас" << endl; }
};
class Tuba : public Bass
{
public:
void boom() { cout << "Туба" << endl; }
};
void DoThis(Bass *tute)
{
tute->boom();
}
int main()
{
Tuba tuba;
Bass bass;
DoThis(&bass);
DoThis(&tuba);
}
Листинг 5.40. Пример — музыка
274
Классы
Пример: фирма
Вернемся еще раз к классу Person и его производным, которые были
созданы на стр. 261 в теме «Наследование».
275
Глава 5
class Person
{
public:
virtual void payment(float money);
};
class employee : public Person
{
public:
virtual void payment(float money);
};
class customer : public Person
{
public:
virtual void payment(float money);
};
class supplier : public Person
{
public:
virtual void payment(float money);
};
void Payment(Person &Man, float Summ)
{
Man.payment(Summ);
}
int main()
{
supplier Ivanov, Stepanov;
276
Классы
customer Kuznecov;
employee Vladimirov;
Payment(Ivanov, 100);
Payment(Stepanov, 100);
Payment(Kuznecov, 100);
Payment(Vladimirov, 100);
}
Листинг 5.41. Платеж
277
Глава 5
278
Классы
Виртуальный деструктор
279
Глава 5
class Bass
{
public:
virtual void boom() = 0;
};
Листинг 5.42. Абстрактный базовый класс
280
Классы
Полиморфизм на практике
При изучении полиморфизма иногда создается впечатление, что его
можно использовать только в редких, очень конструктивных случаях.
Но это совершенно не верно. Многие библиотеки классов вообще не-
мыслимы без полиморфизма. Особенно много классов приложений,
окон и диалоговых окон они предлагают для графических интерфейсов.
Чтобы вставить в приложение окно или диалоговое окно, следует соз-
дать на основании базовых классов собственные и добавить элементы,
которые отличают вашу программу от стандартного шаблона.
Как пример представляется класс CDialog класса MFC (Microsoft
Foundation Classes). Диалоговый класс сам по себе — особый случай
окна и является производным от класса CWnd. Если вы хотите создать
для своей программы собственное диалоговое окно, операционная си-
стема проинформирует вас обо всех событиях, которые произойдут
в связи с этим. Большинство из них вас вообще не интересует, они мо-
гут рассматриваться классом CDialog. Прежде всего вас интересуют
три события. Первое — это открытие диалогового окна. Как только оно
открывается, программа должна инициализировать контрольные эле-
менты, например, заполнять Listbox. Второе событие — это нажатие
пользователем кнопки OK, что означает подтверждение правильности
содержимого диалогового окна. В этот момент нужно прочесть содер-
жимое контрольных элементов и передать его переменным програм-
мы. Третье событие наступает, когда пользователь завершает диалог
клавишей Esc. В данном случае не существует длительной обработки
каких-либо данных, но программа хочет знать, не передумал ли поль-
зователь.
Для обработки этих трех событий класс CDialog предлагает элемент-
ную функцию OnInitDialog() для старта диалогового окна, функцию
OnOK() для нажатия клавиши OK и функцию OnCancel() для обработки
281
Глава 5
BOOL tMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// создание собственного диалога
...
}
Листинг 5.44. Диалоговая инициализация
282
Классы
Вероятно, вас может удивить, почему здесь слово bool внезапно на-
писано прописными буквами. Ответ в том, что сотрудники корпорации
Microsoft создали собственный тип, потому как тип bool на тот момент
времени еще не использовался в качестве стандартного.
283
Глава 5
• Доступ
С помощью ключевых слов public, private и protected обознача-
ется тип доступа к элементам. Если указание такого рода отсутству-
ет, то по умолчанию все элементы private.
• Объявление переменных
Этим устанавливаются элементы класса. Объявление переменных
здесь не отличается от их объявления, например, в функциях. В не-
которых случаях инициализация проводится не объявлением, а с по-
мощью конструктора.
• Объявление функций
Если функция полностью определяется в классе, она компилирует-
ся в качестве встроенной (см. стр. 183).
• Определение функций
В большинстве случаев элементные функции только объявляются
внутри определения класса. Это означает, что определяются они
в другом месте.
• Объявление абстрактных функций
В отличие от обычной, абстрактная функция является виртуальной
и может быть инициализирована 0.
Глава 6
ИНСТРУМЕНТЫ
ПРОГРАММИРОВАНИЯ
Абсолютно все равно, насколько хорошо вы знаете
язык, без компилятора и компоновщика нельзя
создать программу.
285
Глава 6
286
Инструменты программирования
• -lИмя
(строчная l) Использует библиотеку libИмя.а. Поиск файла будет
выполняться по стандартному пути (например /usr/lib) и по допол-
нительному пути -L.
• -g
К коду добавляется информация для отладчика. Так можно в отлад-
чике обратиться к переменным и функциям по их именам.
• -DИмя
С помощью этого свойства можно определить имена. Такая коман-
да равнозначна #define. Если имени требуется присвоить значение,
следует указать -DИмя=Значение.
в коде, где она находится. После чего следует обозначение, ошибка это
или предупреждение. И, наконец, появляется само сообщение об ошиб-
ке. Перед ошибкой в строке 174 находится указание на то, в какой функ-
ции она была обнаружена. Далее представлено несколько типичных со-
общений об ошибках:
• Отсутствует определение
Если используется переменная, для которой отсутствует опреде-
ление, будет получено сообщение об ошибке. Может быть, что вы
действительно забыли определить переменную. Но может также
быть, что вы допустили ошибку при написании ее имени. А может
случиться, что вы определили переменную, но где-то в другом месте.
Компилятор должен знать, как определена переменная, перед ее пер-
вым использованием в программе. Определение следует размещать
перед использованием, или определять прототип. Такое сообщение
об ошибке может касаться не только переменной. Может быть, что
вы вызываете функцию, которая ни определена, ни объявлена.
• Неиспользуемая переменная
Если переменная определена, но ни разу не используется, компи-
лятор «удивится подобному расточительству», и сообщит об этом.
Такого же мнения он будет, если переменной в программе только
присваивается некоторое значение, и больше ничего. Эти предупре-
ждения помогают навести порядок в коде, если после значительных
изменений остаются лишние переменные.
main.cpp:161: Ошибка: >>class Database<< не имеет элемен-
тов с именем "writeURL"
• Отсутствует элемент класса
В этом случае компилятору также не хватает определения. Он мо-
жет отметить, что функция writeURL() хоть и обрабатывается в ка-
честве элемента класса Database, однако не определена в нем.
• …expected/ожидается
Сообщение об ошибке, которое содержит слово «ожидается», или
его английский вариант «expected», указывает, что компилятор не-
сколько удивлен обнаружением определенного элемента в этом ме-
сте. Следующее сообщение появилось по причине отсутствующей
скобки в операторе if.
Queue.cpp:30: Ошибка: expected '(' before >>myValue<<
288
Инструменты программирования
Ошибки компоновщика
Если компилятор удовлетворен исходным кодом, в действие всту-
пает компоновщик. Его задача — из отдельных модулей и библиотек
составить полноценную программу. Соответственно, его ошибками, как
правило, являются отсутствующие функции или глобальные перемен-
ные, которые обещаны в заголовочном файле перед реализацией.
/home/arnold/src/cpp/main.cpp:181: undefined reference
to 'Queue::getInstance()'
Сообщение указывает, что член-функция getInstance() класса
Queue не определена, хотя и вызывается в файле main.cpp в строке 181.
Причины таких ошибок могут быть следующее:
• Если при обработке кода компилятором уже было выдано предупре-
ждение, то имя функции может быть неверно написано или отсут-
ствовать команда #define
• Библиотека не подключена. В интегрированной среде разработки
библиотеки должны подключаться в настройках проекта, если они
не являются стандартными. При использовании инструмента make
(см. стр. 309) или при прямом вызове командной строкой, перед име-
нем библиотеки должно быть указано свойство -l (строчная буква L).
• Библиотека располагается в каталоге, где компоновщик не ищет би-
блиотеки. Если библиотека не расположена по стандартному пути,
ее каталог должен быть внесен в настройки проекта или указан в ко-
мандной строке компилятора со свойством -L.
• Компоновщик привязывает библиотеки только тогда, когда суще-
ствует открытое указание это делать. При этом компоновщик ра-
ботает быстро, а код программ становится короче, но программист
должен следить, чтобы зависимые друг от друга файлы вызывались
компоновщиком в правильной последовательности.
6.2. Препроцессор
Препроцессор унаследован от языка C. Перед тем, как компилятор
получает исходный текст программы, его просматривает препроцес-
289
Глава 6
#define DEBUGCODE
290
Инструменты программирования
#define VERSION 5
Макрос
Возможности препроцессора заходят дальше. Он позволяет создать
нечто наподобие маленькой функции. Следующая команда #define
определяет квазифункцию ADD(), которая складывает два передавае-
мых значения.
#define FALSEINCR(a) a + 1
...
int i = FALSEINCR(4) * 2;
Листинг 6.1. Неожиданный результат
291
Глава 6
int i = 4 + 1 * 2;
#define BEGIN {
#define END }
292
Инструменты программирования
#undef DEBUGCODE
#if defined(DEBUGCODE)
...
#endif
#ifdef HUHU
// разные команды
/* также с комментариями */
#endif
#if MAX<10
293
Глава 6
Макрос Определение
_LINE_ Текущая строка кода источника
_FILE_ Имя файла источника
_DATE_ Текущая дата в формате ММ\ДД\ГГГГ
_TIME_ Текущее время в формате чч:мм:сс
Макрос Определение
unix UNIX-системы, в т.ч. и Linux
_MSDOS_ MS DOS
_Windows MS Windows
294
Инструменты программирования
#ifdef __unix__
fork();
#endif
#error
#error Код, который потом будет выведен
296
Инструменты программирования
g++ -c bermuda.cpp
#include "ship.h"
#include "field.h"
g++ -c ship.cpp
297
Глава 6
298
Инструменты программирования
#ifndef SHIP_H
#define SHIP_H
class tShip {
...
};
#endif
299
Глава 6
300
Инструменты программирования
extern "C" {
#include <cheader1.h>
#include "cheader2.h"
}
302
Инструменты программирования
class CardGame
{
public:
Card *next_card();
void new_mix();
private:
Card card[MAXCARDS];
};
Листинг 6.4. Класс CardGame
303
Глава 6
class localCardGame;
class CardGame {
public:
Card *next_card();
void new_mix();
CardGame();
~CardGame();
private:
localCardGame *my;
};
Листинг 6.5. Карточная игра со скрытой реализацией
class localCardGame
{
public:
Card card[MAXCARDS];
304
Инструменты программирования
};
CardGame::CardGame()
{
my = new localCardGame;
}
CardGame::~CardGame()
{
delete my;
}
Листинг 6.6. Реализация
Среда Windows
extern "C"
__declspec(dllexport) double __stdcall fkt(double w)
{
...
1
Keiser Richard. C++ mit dem Borland C++ Builder. Springer, Berlin Heidelberg, 2002.
2
Wigard Susanne. Visual C++ 6. bhv, Kaarst, 1999.
307
Глава 6
Среда UNIX/Linux
Напротив, для среды UNIX и Linux функции не должны быть особо от-
мечены, чтобы внутри «Shared Library» их можно было вызвать. Для это-
го требуется установить корректные свойства компилятора1. Следующий
пример динамической библиотеки hello просто выводит слово на экран.
#include <iostream>
using namespace std;
void print_hello()
{
cout << "Hello" << endl;
}
Листинг 6.7. Программа libhello.cpp
void print_hello();
int main()
{
print_hello();
}
Листинг 6.8. Программа usehello.cpp
1 Майкл К. Джонсон, Эрик В. Троан. Разработка приложений в среде Linux. М.: Ви-
льямс, Диалектика, 2007.
308
Инструменты программирования
309
Глава 6
meinprog
310
Инструменты программирования
Цель: зависимости
команда генерации
311
Глава 6
Пример:
try :
cd .. ; pwd
pwd
312
Инструменты программирования
Предопределенный макрос
Несколько строк допустимо обрабатывать одним правилом. На-
пример, использовать $(OBJS) в качестве цели. Одна строка может
разделяться на несколько строк. В команде генерирования допустимо
ссылаться на текущую цель. Для этого существуют предопределенные
макросы, показанные в табл. 6.3 на примере файла main.o.
Макрос Значение
$@ Имя целевого файла (main.o)
$* Базовое имя целевого файла (main)
Правило суффикса
Правила суффикса определяют переход от одного расширения фай-
ла к другому. Такое правило можно узнать по тому, что в качестве цели
друг за другом стоят два расширения.
.source.target:
Типичный переход — от C-источника к объектам. Источники име-
ют расширение .с, а объекты — .о. Соответствующее правило суффикса
имеет вид:
.c.o:
g++ -c $<
313
Глава 6
314
Инструменты программирования
Команда Действие
quit Покинуть отладчик
run Запустить программу
kill Остановка выполнения программы
print переменная Показать содержимое переменных
watch переменная Наблюдение за переменной
list Показать код
break имя_функции Установить точку останова
break номер_строки Установить точку останова
clear имя_функции Удалить точку останова
clear номер_строки Удалить точку останова
next Выполнить следующую строку
step Выполнить следующую строку с возможностью за-
continue хода в функцию
Завершить выполняющийся процесс
315
Глава 6
316
Глава 7
ДРУГИЕ ЭЛЕМЕНТЫ ЯЗЫКА C++
Итак, основы для программирования на языке C++
заложены. Рассмотрено обращение с переменными
и полиморфизм. Также и самоопределение
операторов больше не должно быть
для вас проблемой.
int i=15;
int j=7;
cout << min(i,j) << endl;
Листинг 7.3. Команды шаблонной функции min()
320
Другие элементы языка C++
321
Глава 7
322
Другие элементы языка C++
{
cout << a << endl;
}
}
}
Листинг 7.5. Шаблон класса стека
Stack<int> iStack;
#include <iostream>
using namespace std;
template<class T, int max> class Stack
{
public:
323
Глава 7
324
Другие элементы языка C++
{
if (iStack.pop(&a))
{
cout << a << endl;
}
}
}
Листинг 7.6. Шаблонный класс с параметрами (templstack2.cpp)
Упражнение
• Напишите шаблонный класс Stack заново так, чтобы он был реа-
лизован линейным списком. Для этого можно использовать класс
tStack (см стр. 229). Само собой разумеется, что при этом не требу-
ется ограничение размера. Пример решения приведен на стр. 513.
325
Глава 7
326
Другие элементы языка C++
c = a+4*2;
327
Глава 7
Чтобы одну часть кода разместить под одним пространством имен, ис-
пользуется ключевое слово namespace, после чего следует имя и затем код
в фигурных скобках. Следующий пример мог быть указан в файле util.cpp.
namespace util
{
int counter;
void find_begin(string s)
{
...
}
}
Листинг 7.9. Код в пространстве имен util
namespace util
{
int counter;
void find_begin (string s);
int find_end(string s);
}
Листинг 7.10. Прототип пространства имен util
int util::find_end(string s)
{
...
}
Листинг 7.11. Функция для пространства имен util
7.2.2. Доступ
all = util::find_end(filename);
all += util::counter + file::counter;
Листинг 7.12. Доступ к пространству имен
namespace file=CustomerConfigurationFile;
#include "util.h"
#include <iostream>
using namespace std;
namespace
{
int little_help(int a)
{
cout << "Little help" << a << endl;
}
} // namespace
void util::service()
330
Другие элементы языка C++
{
little_help(12);
}
Листинг 7.13. Анонимное пространство имен (namespace/util.cpp)
namespace util
{
void service();
}
Листинг 7.14. Заголовочный файл (namespace/util.h)
#include "util.h"
int main()
{
util::service();
}
Листинг 7.15. Вызов (namespace/main.cpp)
331
Глава 7
try
{
int divisor=0;
int dividend=1;
int quotient = dividend/divisor; // здесь появляется ошибка!
}
catch (...)
{
cout << "Проблема обнаружена" << endl;
}
Листинг 7.16.
332
Другие элементы языка C++
#include <iostream>
using namespace std;
void do_smth(int Problem)
{
333
Глава 7
if (Problem>0)
{
throw 0;
}
}
int main()
{
try
{
do_smth(1);
}
catch(...)
{
cout << "Возникла ошибка!" << endl;
}
}
Листинг 7.17. Исключение, сгенерированное throw
#include <iostream>
using namespace std;
void do_smth(int Problem)
{
if (Problem>0)
{
334
Другие элементы языка C++
throw 5;
}
}
int main()
{
try
{
do_smth(1);
}
catch(int a)
{
cout << "Исключение: " << a << endl;
}
}
Листинг 7.18. Команда throw передает номер ошибки
335
Глава 7
336
Другие элементы языка C++
}
catch(...) // обработка для остальных исключений
{
cout << "Общий случай " << endl;
}
}
Листинг 7.19. Несколько блоков catch (throwtyp.cpp)
#include <iostream>
class no_data_already
{
};
void do_smth(int Problem)
{
...
throw no_data_already();
...
}
int main()
{
try
337
Глава 7
{
do_smth(0);
}
catch(no_data_already& )
{
std::cout << "Информация отсутствует " << std::endl;
}
}
Листинг 7.20. Собственный тип ошибки
#include <iostream>
using namespace std;
class no_data_already
{
public:
no_data_already(int a) { nr = a; }
void show_error() { cout << nr << endl; }
private:
int nr;
338
Другие элементы языка C++
};
void do_smth(int Problem)
{
if (Problem==0)
{
throw no_data_already(8);
}
}
int main()
{
try
{
do_smth(0);
}
catch(no_data_already& error)
{
error.show_error();
}
}
Листинг 7.21. Активный класс ошибки
#include <iostream>
using namespace std;
class meaCulpa // базовый класс ошибок
{
public:
339
Глава 7
340
Другие элементы языка C++
}
catch(meaCulpa& error)
{
error.show_error();
}
}
Листинг 7.22. Полиморфизм класса ошибки (tryclass.cpp)