Академический Документы
Профессиональный Документы
Культура Документы
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)
341
Глава 7
class exception
public:
exception() throw();
};
#include <iostream>
#include <exception>
#include <string>
#include <sstream>
using namespace std;
// мой собственный класс, производный от exception
class meaCulpa : public exception
{
public:
meaCulpa(string s) {this->s = s;}
virtual ~meaCulpa() throw() {}
virtual const char * what() const throw()
{return s.c_str();}
private:
342
Другие элементы языка C++
string s;
};
// особая ошибка, когда отсутствуют данные
class no_data_already : public meaCulpa
{
public:
no_data_already(int a) : meaCulpa(" ") {nr = a;}
virtual ~ no_data_already() throw() {}
virtual const char * what() const throw();
private:
int nr;
string s;
};
// функция what() будет переписана для собственного сообще-
ния об ошибке
const char * no_data_already::what() const throw()
{
ostringstream getNr;
getNr << "Данные отсутствуют. Номер ошибки: " << nr;
return getNr.str().c_str();
}
// Следующий тип ошибки производный от meaCulpa
class missing_source : public meaCulpa
{
public:
missing_source() : meaCulpa("Источник отсутствует") {}
};
// функция do_smth() симулирует оба типа ошибок
// в зависимости от параметра
void do_smth(int Problem) throw (no_data_already, missing_
source)
{
if (Problem==0)
{
343
Глава 7
throw no_data_already(8);
}
if (Problem==1)
{
throw missing_source();
}
}
int main()
{
// ввести номер ошибки
int choice;
cout << "Ввести цифру от 0 до 3:" << endl;
cin >> choice;
// блок try ловит исключение в do_smth()
try
{
do_smth(choice);
}
// идентифицируются только собственные ошибки
catch(meaCulpa& error)
{
cout << error.what() << endl;
}
}
Листинг 7.23. Классы ошибок, производные от класса exeption (exeption.cpp)
344
Другие элементы языка C++
345
Глава 7
#include <stdexcept>
int main()
{
try
{
throw range_error("Что-то здесь тесновато!");
}
catch(range_error& e)
{
cout << e.what() << endl;
}
}
fstream f;
try
{
1
Согласен, несколько необычно, что обсуждение библиотек данных осуществляется
после обработки ошибок. Тем не менее это такая же ситуация, как с курицей и яйцом. Вы
можете сразу перейти к стр. 381, если не хотите ожидать.
346
Другие элементы языка C++
f.exceptions(ios::failbit|ios::badbit);
f.open("test.dat", ios::in);
f << "Этот текст записывается в файл" << endl;
f.close();
}
catch(ios::failure&)
{
if (f.fail()) cout << "fail" << endl;
if (f.bad()) cout << "bad" << endl;
}
Листинг 7.24. Класс fstream с исключениями (fstreamexeption.cpp)
347
Глава 7
01101110
AND 00111100
-------------
00101100
1
Флаги — это параметры, которые могут иметь только два состояния.
348
Другие элементы языка C++
00101101
AND 11110111
-------------
00100101
00101101
XOR 00001000
-------------
00100101
00100101
OR 00001000
-------------
00101101
349
Глава 7
ios::in 00000001
ios::out 00000010
ios::binary 00000100
ios::app 00010000
-----------------------
OR: 00010111
В байте можно передать восемь свойств. Если использовать пере-
менную типа long, возможных вариантов может быть до 32.
350
Другие элементы языка C++
2410 = 25 + 24 = 000110002
351
Глава 7
struct tRegister
{
unsigned int Target:3;
unsigned int reserved1:1;
352
Другие элементы языка C++
StatusByte.Target = 5;
Глава 8
БИБЛИОТЕКИ
«Во время пожара в единственной
библиотеке на Мельмаке сгорели обе книги.
И ни одну из них так и не перерисовали полностью»
(Сериал «Альф»).
354
Библиотеки
355
Глава 8
356
Библиотеки
лов стандартных библиотек языка C++ расширение .h. Класс string на-
ходится в пространстве имен std.
#include <string>
using namespace std;
string myName;
#include <string>
#include <iostream>
using namespace std;
int main()
{
cout << string(40, '=') << endl;
}
Листинг 8.2. Вывод 40 символов равенства
357
Глава 8
Совместимость с С-строками
#include <string>
std::string str = "Инициализация С-строки";
char *cstringPointer = str.c_str();
Присваивание
Строка может быть скопирована в другую переменную типа string
обычным присваиванием. Не требуется никакой специальной функции
strcpy()или циклов с перебором строки поэлементно, не нужно думать
о том, вставлен ли в конец строки завершающий ноль, и контролиро-
вать, хватит ли для копии места в памяти. Можно даже присвоить стро-
ке С-строку без дополнительного конвертирования.
Склеивание строк
Строки можно прикрепить друг к другу, если указать между ними
знак плюс.
#include <string>
using namespace std;
358
Библиотеки
...
string myName = "new";
char oldName[25] = "old";
string newString;
...
newString = myName; // "new"
newString = oldName; // "old"
newString = myName + oldName; // "newold"
newString += oldName; // "newoldold"
#include <string>
#include <iostream>
using namespace std;
int main()
{
string fort = "abc";
string back = "def";
vorn.append(back); // как: fort += back; "abcdef"
}
Доступ к элементу
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str = "Hand";
359
Глава 8
str[1] = 'e';
str[2] = 'l';
str.at(3) = 'p';
cout << str << endl; // "Help"
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "Test";
string::iterator i;
for (i=s.begin(); i!=s.end(); i++)
{
cout << *i ;
}
cout << endl;
}
Листинг 8.3. Считывание строки с помощью итератора (stringit.cpp)
360
Библиотеки
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "Test";
string::reverse_iterator i;
for (i=s.rbegin(); i!=s.rend(); i++)
{
cout << *i ;
}
cout << endl;
}
Листинг 8.4. Пробег в обратном направлении с помощью итератора (stringrev.cpp)
361
Глава 8
Управление строками
Класс string имеет различные функции для изменения символь-
ных последовательностей.
Функция clear() удаляет все символы в строке.
void clear();
Пример:
string str = "Встряхнуть, не перемешивать";
str.clear();
Переменная str в итоге будет содержать пустую символьную после-
довательность.
Функция insert() вставляет строку s в позицию р.
string& insert(size_type p, string& s);
Пример:
string str = "Встряхнуть, не перемешивать";
str.insert(11, "но ");
Переменная str будет хранить символьную строку «Встряхнуть, но
не перемешивать».
Функция erase() удаляет n символов с позиции р.
string& erase(size_type p, size_type n);
Пример:
string str = "Встряхнуть, не перемешивать";
str.erase(11, 4);
Переменная str будет содержать строку «Встряхнуть, перемеши-
вать».
Функция replace() заменяет, начиная с позиции р, количество
символов n в строке s. Изменяемая строка дополнительно возвращается
в качестве ссылки.
362
Библиотеки
Сравнение
В отличие от С-строк, объекты string могут сравниваться с по-
мощью обычных операторов сравнения, которые уже знакомы нам из
сравнения числовых значений (табл. 8.1). Не требуется больше никаких
специальных функций, вроде strcmp().
Оператор Значение
== Равно
!= Не равно
< Стоит впереди в алфавите
> Стоит после в алфавите
<= Стоит впереди или на той же позиции в алфавите
>= Стоит позади или на той же позиции в алфавите
363
Глава 8
string s;
char cstring[256];
...
if (s < cstring)
...
if (cstring < s)
Поиск и информация
364
Библиотеки
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str = "Карл у Клары украл кораллы"
"Клара у Карла украла кларнет";
string such = "украл";
unsigned int pos = 0;
while (pos!=string::npos)
{
pos = str.find(such, pos);
if (string::npos==pos)
{
cout << "не найдено!" << endl;
} else {
cout << pos << endl;
++pos; // предотвращение бесконечного цикла
}
}
}
Листинг 8.5. В поиске
365
Глава 8
#include <sstream>
using namespace std;
int my_value = 0;
string s("123");
istringstream inStream(s);
inStream >> my_value;
#include <sstream>
using namespace std;
366
Библиотеки
1
Kaiser Richard. C++ mit dem Borland C++ Builder. Springer, Berlin Heidelberg, 2002.
367
Глава 8
while(*source)
{
*target++ = *source++;
}
*target = 0;
Листинг 8.7. Функция strcpy(), реализованная самостоятельно
369
Глава 8
370
Библиотеки
char *source;
char target[MAX];
...
strncat(target, source, MAX-strlen(Ziel));
target[MAX-1] = 0; // надежно и никогда не помешает
#include <string.h>
char *strcat(char *target, const char *source);
int strcmp(const char *s1, const char *s2);
char *strcpy(char *target, const char *source);
size_t strlen(const char *s);
char *strncat(char *target, const char *source, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);
char *strncpy(char *target, const char *source, size_t n);
char *strstr(const char * haystack, const char * needle);
371
Глава 8
1
Слово «atoi» является сокращением от «ascii to int».
372
Библиотеки
Упражнение
• Напишите функцию atof() с такими же параметрами, как показано
выше. Используйте при этом функции строк, чтобы найти запятую
и заменить ее точкой и вызвать после этого стандартную функцию
atof(). Пример решения на стр. 515.
#include <stdio.h>
int printf(const char *Format, ...);
int fprintf(FILE *stream, const char *Format, ...);
int sprintf(char *String, const char *Format, ...);
sprintf(OutStr,
"%s %s имеет клиентский номер %d и %7.2f EUR на счету",
FirstnameStr, NameStr, ClNr, Money);
1
От англ. «print formatted» — форматированный вывод. Прим. ред.
374
Библиотеки
Флаг Обозначение
0 Заполнение нулями
− Выравнивание по левому краю
Пробел Вывод пробела вместо положительного знака числа
+ Выводить знак числа и для положительных значений
375
Глава 8
#include <iostream>
#include <string>
using namespace std;
int main()
{
char a[5], b[20]; // или: string a, b;
cin >> a >> b;
cout << a << "," << b << endl;
}
Листинг 8.9. Чтение символьной последовательности
Считывание строки
1
От англ. словосочетания «end of file» — конец файла. Прим. ред.
377
Глава 8
int main()
{
char theString[MAXSTR];
cin.getline(theString, sizeof(theString));
cout << theString << endl;
}
8.2.2. Манипуляторы
378
Библиотеки
725
-----725
725-----
1325
----1325
2d5-----
379
Глава 8
Манипулятор Действие
endl Добавляет переход на новую строку
setw(n) Ширина столбца для следующего выводимого объекта равна n
left Выравнивание по левому краю
right Выравнивание по правому краю
setfill(c) Использовать в качестве символа заполнения пустых позиций
dec Десятичное представление
oct Восьмеричное представление
hex Шестнадцатеричное представление
showbase Выводит 0х перед шестнадцатеричным числом
boolalpha Выводит true и false в кодовом виде
noboolalpha Выводит true как 1, а false как 0
Манипулятор Действие
showpoint Представление с десятичной точкой и позициями после за-
noshowpoint пятой
scientific Выводится только значащая часть
fixed Число с плавающей запятой в математическом виде
setprecision(n) Фиксированные позиции после запятой
Точность; n — количество позиций
1
Стефан Р. Дэвис. С++ для чайников. М.: Диалектика, Вильямс, 2009.
380
Библиотеки
ios& left(ios& i)
{
i.setf(ios::left);
return i;
}
Листинг 8.12. Самостоятельно написанный манипулятор left
1
Jakobs Holger. Einführung in ISO C++, PDF-Datei v. 12.4.2000.
381
Глава 8
#include <fstream>
using namespace std;
int main()
{
fstream f;
f.open("test.dat", ios::out);
f << "Этот код записывается в файл" << endl;
f.close();
}
Листинг 8.13. Запись кода в файл
382
Библиотеки
Константа Значение
ios::in Для чтения
ios::out Для записи
ios::trunk Файл будет очищен при открытии
ios::app Записываемые данные помещать в конец файла
ios::ate Указатель позиции установить на конец
383
Глава 8
Как правило, язык C++ исходит из того, что файл представлен кодом.
Из-за этого переходы на следующую строку организуются по-разному
в зависимости от платформы.
Смысл и цель файла состоят в том, чтобы хранить данные. При этом
кодовые файлы отличаются от файлов данных. Кодовые файлы можно
обработать операторами ввода и вывода. Их можно читать и обрабаты-
вать любым редактором. Файлы данных, напротив, обычно определяют-
ся блоками определенной длины и так же считываются.
Поток данных
384
Библиотеки
Оператор ввода >> работает с объектом fstream так же, как с cin.
Файл считывается так, словно информация вводится с клавиатуры.
Сюда же относится то, что пробел, табуляция и переход на следующую
строку интерпретируются в качестве разрыва ввода. Это действительно
и в ситуации, когда поток данных записывается в переменную символь-
ной последовательности.
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
fstream f;
char cstring[256];
f.open(argv[1], ios::in);
while (!f.eof())
{
f.getline(cstring, sizeof(cstring));
cout << cstring << endl;
}
f.close();
}
Листинг 8.14. Считывание файла
385
Глава 8
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
ifstream f; // объект файла
string s;
// открыть файл с заданными параметрами
f.open(argv[1], ios::in);
while (!f.eof()) // пока файл не закончился
{
getline(f, s); // считать строку
cout << s << endl; // вывести ее на экран
}
f.close(); // файл снова закрыть
}
Листинг 8.15. Считывание файла (getline.cpp)
Файлы данных
Для записи двоичных файлов способ передачи данных менее приго-
ден. Здесь используются функции read() и write(), с помощью кото-
рыхзаписываются и считываются четкие блоки данных. Для них в каче-
стве главного буфера памяти используются чаще всего объекты данных
или структуры.
tData Data;
fstream f(..., ios::out|ios::binary|ios::in);
f.write(&Data, sizeof(Data));
...
f.read(&Data, sizeof(Data));
386
Библиотеки
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
#include <string.h>
// этот класс принимает символьную последовательность в
// классический массив char и хранит при этом данные.
class tData
{
public:
void Set(char *para)
{
387
Глава 8
388
Библиотеки
{
Data.Set(argv[1]); // поместить в Data
// сохранить объект в файле
f.write((char *)&Data, sizeof(Data));
}
// аргумент отсутствует? Тогда прочесть файл!
if (argc==1)
{
// считать данные из файла в объект
f.read((char *)&Data, sizeof(Data));
Data.Show(); // ... и вывести
}
}
Листинг 8.16. Тест: сохранение структур данных (teststr.cpp)
389
Глава 8
Константа Значение
ios::beg Рассматривать с начала файла
ios::cur Рассматривать с текущей позиции
ios::end Рассматривать с конца файла
fstream File;
...
if (File.good())
{
// все отлично
...
if (File)
{
// тоже хорошо
390
Библиотеки
391
Глава 8
#include <stdio.h>
int main()
{
FILE *f;
f = fopen("test.dat", "rwb");
if (f)
{
fwrite(buffer, sizeof(buffer), 1, f);
fseek(f, 0, SEEK_SET);
fread(buffer, sizeof(buffer), 1, f);
fclose(f);
}
}
Листинг 8.17. Работа с файлом
393
Глава 8
Символ Значение
r Открыть для чтения
w Удалить содержимое и открыть для записи
a Данные будут добавлены
r+ Помимо чтения разрешить также запись
w+ Удалить содержимое и открыть для записи и чтения
b Двоичный файл (преобразование символов окончания строк отсут-
ствует)
394
Библиотеки
Константа Значение
SEEK_SET Начало файла
SEEK_CUR Текущая позиция
SEEK_END Конец файла
#include <stdio.h>
int remove(const char *filename);
395
Глава 8
Константа Значение
EACCES Не хватает прав для записи
ENOENT Файл не существует
EROFS Файл находится в среде, защищенной от записи
Переименование: rename()
#include <stdio.h>
int rename(const char *filename, const char *newname);
Другие функции
396
Библиотеки
Функция Значение
mkdir Создание каталога
chdir Смена каталога
rmdir Удаление пустого каталога
opendir Открывает каталог для поиска файла
readdir Считывает следующую запись в каталоге
closedir Закрывает каталог
#include <dirent.h>
#include <iostream>
using namespace std;
int main()
{
DIR *hdir;
struct dirent *entry;
hdir = opendir(".");
do
{
entry = readdir(hdir);
if (entry)
{
cout << entry->d_name << endl;
}
} while (entry);
closedir(hdir);
}
Листинг 8.18. Считывание каталога (dir.cpp)
397
Глава 8
#include <sys/types.h>
#include <sys/stat.h>
int stat(char *filename, struct stat *buffer);
struct stat {
dev_t st_dev /* (P) устройство, на котором хранится файл
*/
ushort st_ino /* (P) индексный дескриптор файла */
ushort st_mode /* (P) тип файла */
short st_nlink /* (P) количество ссылок файла */
ushort st_uid /* (P) владелец файла User-ID (uid) */
ushort st_gid /* (P) ID группы (gid) */
1
В Visual C++ такая структура имеет имя _stat.
398
Библиотеки
399
Глава 8
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
struct stat Status;
stat(argv[1], &Status);
printf("Права доступа: %o \n", Status.st_mode & ~S_IFMT);
switch(Status.st_mode & S_IFMT) {
case S_IFREG: puts("Файл"); break;
case S_IFLNK: puts("Символьная ссылка"); break;
case S_IFDIR: puts("Каталог"); break;
default: puts("Другое");
}
}
Листинг 8.19. Определение типа файла и прав доступа
400
Библиотеки
401
Глава 8
Тригонометрические функции
Объявление Функция
double acos(double); Арккосинус
double asin(double); Арксинус
double atan(double); Арктангенс
double atan2(double, double); Арктангенс двух переменных
double cos(double); Косинус
double cosh(double); Гиперболический косинус
double sin(double); Синус
double sinh(double); Гиперболический синус
double tan(double); Тангенс
double tanh(double); Гиперболический тангенс
402
Библиотеки
Другие функции
Функция абсолютного значения возвращает значение передавае-
мой величины, если она положительна, и умножает ее на –1, если от-
рицательна. То есть функция всегда возвращает только положительное
значение. Существует функция abs() для целых чисел из библиотеки
stdlib и функция fabs() для чисел с плавающей запятой из математи-
ческой библиотеки.
#include <stdlib.h>
int abs(int j);
long labs(long k);
Функция fabs() отличается от abs() типом параметров возвращае-
мого значения и тем, что для ее использования требуется подключить
файл math.h.
1 Сокращение происходит от английского выражения «square root» (квадратный
корень).
403
Глава 8
#include <math.h>
double fabs(double a);
Расчет остатка для целых чисел осуществляется оператором %. Он
возвращает остаток от их деления. Для чисел с плавающей запятой та-
кой расчет выполняется функцией fmod().
double fmod(double a, double b);
Значение с плавающей запятой a раскладывается в функции modf()
на целую и дробную части. Целая записывается в переменную b, а дроб-
ная является возвращаемым значением функции.
double modf(double a, int* b);
Функция ceil() возвращает округленное к большему значению
целое число.
double ceil(double);
Функция floor() возвращает округленное к меньшему значению
целое число.
double floor(double);
#include <complex>
using namespace std;
complex<double> myComplex(-1, 3);
404
Библиотеки
Функция Действие
norm() Возвращает квадрат абсолютного значения комплексного числа
abs() Абсолютное значение, корень из функции norm()
conj() Комплексное сопряженное число
arg() Угол в полярных координатах
polar() Комплексное число для полярных координат
#include <time.h>
time_t time(time_t *t);
405
Глава 8
#include <time.h>
struct tm *localtime(const time_t *timer);
#include <iostream>
using namespace std;
#include <time.h>
int main()
{
time_t TimeLabel;
tm *now;
TimeLabel = time(0);
406
Библиотеки
now = localtime(&TimeLabel);
cout << now->tm_mday << '.' << now->tm_mon+1 << '.'
<< now->tm_year+1900 << " - " << now->tm_hour
<< ':' << now->tm_min << endl;
}
Листинг 8.20. Вывод времени
#include <time.h>
char *asctime(const struct tm *t);
char *ctime(const time_t *timep);
#include <sys/time.h>
int gettimeofday(struct timeval *time, void *tp);
struct timeval {
unsigned long tv_sec; /* секунды с 1.1.1970 */
long tv_usec; /* микросекунды */
};
407
Глава 8
#include <iostream>
using namespace std;
#include <sys/time.h>
int main()
{
timeval start, end;
gettimeofday(&start, 0);
...
gettimeofday(&end, 0);
cout << start.tv_sec << ':' << start.tv_usec << endl;
cout << end.tv_sec << ':' << end.tv_usec << endl;
}
Листинг 8.21. Сообщение времени работы программы
#include <time.h>
clock_t clock(void);
408
Библиотеки
#include <iostream>
using namespace std;
#include <time.h>
int main()
{
clock_t start, end;
start = clock();
...
end = clock();
cout << start << endl;
cout << end << ':' << CLOCKS_PER_SEC << endl;
}
Листинг 8.22. Измерение тактов центрального процессора
Глава 9
СТАНДАРТНАЯ БИБЛИОТЕКА
ШАБЛОНОВ (STL)
STL — стандартная библиотека. Она предлагает
структуры данных и методы, которые ранее снова
и снова разрабатывались программистами заново.
Между тем от каждого профессионального
программиста C++ ожидается использование STL.
410
Стандартная библиотека шаблонов (STL)
#include <vector>
#include <iostream>
using namespace std;
const int MAX = 8;
int main()
{
vector<int> myvec(MAX); // вектор для 8 целых значений
myvec[0] = 9; // как в массиве
myvec[3] = 9;
for (int i=0; i<MAX; ++i)
{
cout << i << ": " << myvec[i] << endl;
}
}
Листинг 9.1. Вектор для целых значений
Все выглядит так же, как в массиве. Только лишь описание позво-
ляет определить происхождение вектора от класса шаблона. Создается
шаблон vector типа int и в конструкторе параметров задается размер.
Затем его можно легко изменить. Также размер можно опустить. Тог-
да следует вовремя увеличить вектор, прежде чем записывать в него
данные.
411
Глава 9
#include <vector>
using namespace std;
...
int MaxVec = 5;
vector<int> Numbers(MaxVec);
...
Numbers[MaxVec+1] = 12; // не будет воспринято
Numbers.at(MaxVec+1) = 12; // будет проверено
1
Тема обработки исключений рассмотрена на стр. 331.
412
Стандартная библиотека шаблонов (STL)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> values;
values.push_back(8);
values.push_back(7);
values.push_back(6);
do
{
cout << ": " << values.back();
values.pop_back();
} while (values.size());
cout << endl;
}
Листинг 9.2. Функция стека для вектора
413
Глава 9
9.1.2. Итераторы
Итераторы ведут себя точно так же, как и указатели. Они исполь-
зуются для получения доступа к элементу контейнера. Классы контей-
неров предлагают к использованию итераторы для доступа к элементу
контейнера. Итератор создается через контейнер и тип элемента. Чтобы
объявить итератор для вектора целых значений можно использовать
следующую строку:
vector<int>::iterator it;
#include <iostream>
#include <vector>
using namespace std;
414
Стандартная библиотека шаблонов (STL)
int main()
{
int MaxVec = 5;
int i=0;
vector<int> values(MaxVec);
vector<int>::iterator it = values.begin();
while(it!=values.end())
{
*it = i++;
// соответствует: values[i] = i++;
it++;
}
for (it = values.begin(); it!= values.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
Листинг 9.3. Перемещение сквозь вектор с помощью итератора
415
Глава 9
Функция Действие
obj& operator[] Непроверяемый доступ к элементу
obj& at(size_type) Проверяемый доступ к элементу
obj& front() Возвращает первый элемент вектора
obj& back() Возвращает последний элемент вектора
size_type size() Возвращает занимаемый размер вектора
size_type max_size() Возвращает максимальный размер
void resize(size_type size) Изменяет размер на значение size
size_type capacity() Возвращает размер резервированной памяти
bool empty() Проверяет, пуст ли вектор
reserve(size_type size) Изменяет емкость памяти на n-элементов
void push_back(&obj) Добавляет объект в конец вектора
void pop_back() Удаляет объект в конце вектора
obj& front() Возвращает ссылку на элемент в начале
списка
obj& back() Возвращает ссылку на элемент в конце
списка
iterator begin() Возвращает итератор первого элемента
iterator end() Возвращает итератор позиции после послед-
него элемента
iterator rbegin() Возвращает итератор последнего элемента
iterator rend() Возвращает итератор позиции перед первым
элементом
void assign( InputIterator Присваивает вектору данные, которые раз-
first, InputIterator last) мещены между двумя итераторами
void insert(iterator, obj&) Добавляет объект в позицию итератора
416
Стандартная библиотека шаблонов (STL)
Функция Действие
iterator erase(iterator first, Удаляет область, ограниченную итераторами
iterator last)
pop_back()
12 13 14 15 16 17 18 19 20 21 back()
push_back()
pop_front()
11 10 9 8 7 6 5 4 3 2 1 0 front()
push_front()
Рис. 9.1. Очередь
1
От англ. «double ended queue» — двунаправленная очередь.
417
Глава 9
#include <iostream>
#include <deque>
using namespace std;
#include <time.h>
int main()
{
clock_t start, end;
start = clock();
deque<int> buffer;
for(int i=0; i<1000; i++)
{
for (int j=1; j<1000; j++)
{
buffer.push_front(j);
}
do
{
buffer.pop_back();
} while (buffer.size());
}
end = clock();
cout << start << ":" << end << endl;
}
Листинг 9.4. Очередь FIFO (stl/deque.cpp)
1
Акроним от фразы First In, First Out — «первым пришел — первым ушел». Прим. ред.
418
Стандартная библиотека шаблонов (STL)
Функция Действие
obj& operator[] Непроверяемый доступ к элементу
obj& at(size_type) Проверяемый доступ к элементу
obj& front() Возвращает первый элемент вектора
obj& back() Возвращает последний элемент вектора
size_type size() Возвращает занимаемый размер вектора
size_type max_size() Возвращает максимальный размер
void resize(size_type size) Изменяет размер на значение size
bool empty() Проверяет, пуст ли вектор
void push_back(&obj) Добавляет объект в конец вектора
void pop_back() Удаляет объект в конце вектора
void push_front(obj) Добавляет элемент в начало списка
void pop_front() Удаляет элемент в начале списка
obj& front() Возвращает ссылку на элемент в начале списка
obj& back() Возвращает ссылку на элемент в конце списка
iterator begin() Возвращает итератор первого элемента
iterator end() Возвращает итератор позиции после последнего
элемента
419
Глава 9
Функция Действие
iterator rbegin() Возвращает итератор последнего элемента
iterator rend() Возвращает итератор позиции перед первым
элементом
void assign( InputIterator Присваивает вектору данные, которые размещены
first, InputIterator last) между двумя итераторами
void insert(iterator, obj&) Добавляет объект в позицию итератора
iterator erase(iterator Удаляет область, ограниченную итераторами
first, iterator last)
iterator erase(iterator) Удаляет элемент, на который указывает итератор
void swap(vector<T, Обменивает содержимое двух векторов
Allocator>&vec)
void clear() Удаляет все элементы и вызывает их деструкторы
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> count;
list<int>::iterator lIter; // объявлен итератор
srand(0);
// в список будет записано 10 случайных значений
for(int i=0; i<10; i++)
{
count.push_back( rand() % 9 + 1 );
}
// перебрать и вывести
for(lIter=count.begin(); lIter!=count.end(); lIter++)
{
cout << *lIter << endl;
}
// перед каждым числом 8 добавить -1, удалить каждое число 2
for(lIter=count.begin(); lIter!=count.end(); lIter++)
{
if (8==*lIter)
{
count.insert(lIter, -1);
}
else if (2==*lIter)
{
J lIter = count.erase(lIter);
}
}
// перебрать и вывести
422
Стандартная библиотека шаблонов (STL)
423
Глава 9
target.splice(pos, source);
target.merge(source);
list<int> source;
...
bool vergl(int a, int b)
return a<b;
..,
target.merge (source, vergl);
list<int> source;
...
bool vergl(int a, int b)
return a<b;
..,
target.sort (vergl);
Функция Действие
obj& front() Возвращает первый элемент вектора
obj& back() Возвращает последний элемент вектора
size_type size() Возвращает занимаемый вектором размер
size_type max_size() Возвращает максимальный размер
void resize(size_type size) Изменяет размер на значение size
bool empty() Проверяет, пуст ли вектор
void push_back(&obj) Добавляет объект в конец вектора
void pop_back() Удаляет объект в конце вектора
void push_front(obj) Добавляет объект в начало вектора
void pop_front() Удаляет объект в начале вектора
obj& front() Возвращает ссылку на элемент в начале списка
obj& back() Возвращает ссылку на элемент в конце списка
426
Стандартная библиотека шаблонов (STL)
#include <iostream>
#include <set>
using namespace std;
void show(set<int> &count)
{
set<int>::iterator iter;
for(iter=count.begin(); iter!=count.end(); ++iter)
{
cout << *iter << " - " ;
}
cout << endl;
}
427
Глава 9
int main()
{
set<int> count;
int new_number;
srand(0);
for(int i=0; i<10; i++)
{
new_number = rand() % 9 + 1 ;
count.insert(new_number);
cout << new_number << " - " ;
}
cout << endl;
show(count);
count.erase(4);
show(count);
}
Листинг 9.6. Добавление и удаление в классе set (stl/set.cpp)
428
Стандартная библиотека шаблонов (STL)
class other
{
public:
bool operator() (int a, int b)
{
return a > b;
}
};
void show(set<int,other > &values)
{
set<int>::iterator iter;
for(iter=values.begin(); iter!=values.end(); ++iter)
{
cout << *iter << " - " ;
}
cout << endl;
}
int main()
{
set<int,other > values;
...
Листинг 9.7. Класс сравнения (stl/setsort.cpp)
429
Глава 9
Функция Действие
size_type size() Возвращает занимаемый вектором объем
size_type max_size() Возвращает максимальный объем
bool empty() Проверяет, пуст ли вектор
iterator begin() Возвращает итератор первого элемента
iterator end() Возвращает итератор последнего элемента
iterator rbegin() Возвращает итератор позиции перед первым
элементом
iterator rend() Возвращает итератор позиции после последнего
элемента
void insert(iterator, obj&) Добавляет объект в позицию итератора
iterator erase(iterator first, Удаляет область, ограниченную итераторами
iterator last)
iterator erase(iterator) Удаляет элемент, на который указывает итератор
void swap(vector<T, Обменивает содержимое двух векторов
Allocator>&vec)
void clear() Удаляет все элементы и вызывает их деструкторы
key_compare key_comp() Возвращает объект сравнения контейнера
value_compare value_comp() Возвращает значение объекта сравнения контей-
нера
430
Стандартная библиотека шаблонов (STL)
Так при определении контейнера map задаются два типа: первый для
ключа и второй для значения.
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
map<string,string> number;
number["HH"] = "Hansestadt Hamburg";
cout << number["HH"] << endl;
cout << "Размер: " << number.size() << endl;
cout << number["HG"] << endl;
cout << "Размер: " << number.size() << endl;
}
Листинг 9.8. Пример класса map (stl/map.cpp)
Hansestadt Hamburg
Размер: 1
Размер: 2
if (number.find("KI")== number.end())
{
cout << "Не найдено!" << endl;
}
431
Глава 9
Функция Действие
obj& operator[](key_type&) Доступ к объекту по ключу
size_type size() Возвращает занимаемый размер вектора
size_type max_size() Возвращает максимальный размер
bool empty() Проверят, пуст ли вектор
iterator begin() Возвращает итератор первого элемента
iterator end() Возвращает итератор позиции после последнего
элемента
iterator rbegin() Возвращает итератор последнего элемента
iterator rend() Возвращает итератор позиции перед первым эле-
ментом
void insert(iterator, obj&) Добавляет объект в позицию итератора
iterator erase(iterator Удаляет область между двумя итераторами
first, iterator last)
iterator erase(iterator) Удаляет элемент, на который указывает итератор
void swap(vector<T, Обменивает содержимое двух векторов
Allocator>&vec)
void clear() Удаляет все элементы и вызывает их деструкторы
iterator find(key_type&) Ищет первый элемент равный параметру
size_type Подсчитывает количество элементов, равных
count(key_type&) параметру
key_compare key_comp() Возвращает объект сравнения контейнера
value_compare Возвращает значение объекта сравнения контейнера
value_comp()
iterator lower_bound() Производит поиск элемента меньшего, чем пара-
метр
iterator upper_bound() Производит поиск элемента большего, чем параметр
pair<iterator,iterator> Возвращает итератор элемента, равного параметру
equal_range( key_type&)
9.6. Контейнер-адаптер
Контейнер-адаптер создает специальные интерфейсы, которые на-
кладываются на контейнеры. Он всегда использует некоторый опреде-
ленный тип контейнера в качестве основы, однако в тоже время может
накладываться на другой контейнер. Так стек, например, может быть
реализован на основе вектора. Стек предлагает пользователю ограни-
ченный интерфейс. Адаптер не использует итераторов.
432
Стандартная библиотека шаблонов (STL)
Функции Действие
void push(obj) Помещает объект в стек
obj& top() Возвращает из стека верхний объект. При этом из стека он не
удаляется.
void pop() Удаляет из стека верхний объект. Функция не возвращает
значений
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> myStack;
myStack.push(3);
myStack.push(2);
myStack.push(1);
while (myStack.empty())
{
cout << myStack.top() << endl;
myStack.pop();
}
}
Листинг 9.9. Адаптер стека (stl/stack.cpp)
433
Глава 9
Функция Действие
void push(obj) Помещает объект в конец буфера
void pop() Удаляет из стека первый записанный в него объект. Возвра-
щаемое значение отсутствует
obj& front() Возвращает первый объект. При этом он не удаляется
obj& back() Возвращает последний объект. При этом он не удаляется
#include <iostream>
//#include <list>
#include <queue>
using namespace std;
int main()
{
queue<int> FIFO;
// queue<int, list<int> > FIFO;
434
Стандартная библиотека шаблонов (STL)
FIFO.push(4);
FIFO.push(3);
FIFO.push(2);
FIFO.push(1);
while (!FIFO.empty())
{
cout << FIFO.front() << endl;
FIFO.pop();
}
}
Листинг 9.10. Контейнер queue (stl/queue.cpp)
#include <iostream>
#include <queue>
using namespace std;
int main()
{
priority_queue<int> FIFO;
435
Глава 9
FIFO.push(4);
FIFO.push(7);
FIFO.push(5);
FIFO.push(1);
while (!FIFO.empty())
{
cout << FIFO.top() << endl;
FIFO.pop();
}
}
Листинг 9.11. Очередь с приоритетом (stl/pqueue.cpp)
436
Стандартная библиотека шаблонов (STL)
Контейнер Итератор
vector RandomAccessIterator
deque RandomAccessIterator
string RandomAccessIterator
list BidirectionalIterator
map BidirectionalIterator
multimap BidirectionalIterator
set BidirectionalIterator
multiset BidirectionalIterator
437
Глава 9
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int values[9] = {1,2,3,4,5,6,7,8,9};
int *found;
int *last = values+9;
found = find(values, last, 4);
if (found!=last)
{
cout << *found << " - " << endl;
}
else
{
cout << "Не найдено!" << endl;
}
}
Листинг 9.13. Поиск числа в массиве (stl/find.cpp)
9.8.2. Сортировка
438
Стандартная библиотека шаблонов (STL)
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int values[9] = {3,8,1,9,5,6,4,2,9};
sort(values, values+9);
for (int i=0; i<9; i++)
{
cout << values[i]<< endl;
}
}
Листинг 9.14. Сортировка массива
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int MaxVec = 9;
vector<int> values(MaxVec);
srand(0);
439
Глава 9
int main()
{
int values[9] = {1,2,4,9,5,6,7,8,9};
int target[5];
copy(values, values+5, target);
}
Листинг 9.16. Копирование
440
Стандартная библиотека шаблонов (STL)
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
char word[80];
copy(argv[1], argv[1]+strlen(argv[1])+1, word);
reverse(word, word+strlen(word));
cout << word << endl;
}
Листинг 9.17. Перестановка передаваемого параметра (stl/reverse.cpp)
441
Глава 9
int main()
{
int values[9] = {1,2,4,9,5,6,7,8,9};
fill(values, values+9, 4);
}
Листинг 9.18. Заполнение
442
Стандартная библиотека шаблонов (STL)
#include <iostream>
#include <algorithm>
using namespace std;
bool is_nine(int parameter)
{
return (parameter==9);
}
int main()
{
int values[9] = {1,2,3,9,5,6,7,8,9};
int *found;
found = find_if(values, values+9, is_nine);
if (found!= values+9)
{
cout << *found << " - " << endl;
}
else
{
cout << "Не найдено" << endl;
}
}
Листинг 9.19. Поиск числа в массиве
443
Глава 9
#include <iostream>
#include <algorithm>
#include <limits.h>
using namespace std;
444
Стандартная библиотека шаблонов (STL)
class tLittle
{
public:
tLittle() { last = INT_MIN; }
bool operator()(int a);
private:
int last;
};
bool tLittle::operator()(int a)
{
bool back_value;
back_value = a < last;
last = a;
return back_value;
}
int main()
{
int value[9] = {1,2,3,9,5,6,7,8,9};
int *found;
tLittle back_case;
found = find_if(value, value+9, back_case);
if (found!=value+9)
{
cout << *found << endl;
}
else
{
cout << "Не найдено" << endl;
}
}
Листинг 9.20. Использование объекта функции (stl/findif.cpp)
445
Глава 9
#include <iostream>
#include <algorithm>
using namespace std;
int show(int a)
{
cout << a << endl;
}
int main()
{
int values[9] = {1,2,4,9,5,6,7,8,9};
for_each(values, values+9, show);
}
Листинг 9.21. Вывод с помощью for_each() (stl/foreach.cpp)
446
Стандартная библиотека шаблонов (STL)
Функция Действие
bool test(size_t pos) Возвращает true, если бит установлен
bitset<N>& set(size_t pos, int var=1) Устанавливает бит в позиции pos
bitset<N>& reset(size_t pos, int var=0) Удаляет бит в позиции pos
bitset<N>& flip(size_t pos) Переключает бит в позиции pos
Функция Действие
size_t count() Количество установленных бит в bitset
size_t size() Количество бит в bitset
bool any() Возвращает true, если установлен хотя бы один бит
bool none() Возвращает true, если не установлен ни один бит
447
Приложение А
ЯЗЫК C++ ДЛЯ ТЕХ,
КТО ТОРОПИТСЯ
А.1. Программа
Первая программа выводит на экран сообщение, требует ввод стро-
ки, рассчитывает квадрат и выводит его значение на экран.
#include <iostream>
using namespace std;
int main()
{
int input;
int Square;
cout << "Введите число: ";
cin >> input;
Square = input * input;
cout << "Квадрат числа равен " << Square << endl;
}
Листинг А.1. Квадрат числа
448
Язык C++ для тех, кто торопится
> ./a.out
Введите число:
int main()
{
449
Приложение А
450
Язык C++ для тех, кто торопится
Тип Данные
int Целые числа
short Целые числа (обычно от -32.768 до 32.767)
long Целые числа (обычно от -2.147.483.648 до 2.147.483.647)
float Числа с плавающей запятой (например -2,5 или 3,14159)
double Числа с плавающей запятой высокой точности
long double Числа с плавающей запятой особо высокой точности
char Буквы или целые числа от -128 до 127
wchar_t UNICODE буквы для интернациональных наборов символов
451
Приложение А
Оператор Значение
+ Сложение
− Вычитание
* Умножение
/ Деление
= Присваивание
<< Передача (обычно после cout)
>> Передача (обычно после cin)
cout << "Квадрат числа равен " << input << endl;
452
Язык C++ для тех, кто торопится
if (divisor==0)
{
cout << "Деление на 0" << endl;
}
else
{
quotient = dividend / divisor;
}
Листинг А.2. Защита от деления на 0
453
Приложение А
454
Язык C++ для тех, кто торопится
Оператор Значение
== Равенство
!= Неравенство
< Меньше
<= Меньше или равно
> Больше
>= Больше или равно
! Отрицание булева выражения (NOT)
&& И-объединение (AND)
|| ИЛИ-объединение (OR)
В цикле команда или блок команд может повторяться так долго, как
это указано в его условии.
#include <iostream>
using namespace std;
int main()
{
int InValue = 0;
while (InValue<1 || InValue>6)
{
cout << "Введите число от 1 до 6!";
cin >> InValue;
}
}
Листинг А.5. Ввод числа
455
Приложение А
#include <iostream>
using namespace std;
int main()
{
int i;
for (i=1; i<=10; i++)
{
cout << i << ": " << i*i << endl;
}
}
Листинг А.6. Квадраты числа
456
Язык C++ для тех, кто торопится
А.3. Массивы
Массив — это объединение нескольких переменных одного типа.
При этом они стоят в ряд друг за другом. Целый массив имеет имя. Что-
бы обратиться к отдельному элементу массива, нужно указать номер его
позиции в квадратных скобках.
5 12 13 28 32 43
457
Приложение А
#include <iostream>
using namespace std;
int main()
{
int lotto[6];
lotto[0] = 5;
lotto[1] = 12;
lotto[2] = 13;
lotto[3] = 28;
lotto[4] = 32;
lotto[5] = 43;
int i;
for (i=0; i<6; i++)
{
cout << lotto[i] << " ";
}
cout << endl;
}
Листинг А.7. Числа лото
458
Язык C++ для тех, кто торопится
#include <iostream>
using namespace std;
int main()
{
char Name[80];
cout << "Как Вас зовут?" << endl;
cin.getline(Name, 80);
cout << "Добрый день, " << Name << "!" << endl;
char Copy[80];
int i;
for (i=0; Name[i] && i<79; i++)
{
Copy[i] = Name[i];
}
Copy[i] = 0;
cout << "Это Ваша копия: " << Copy << endl;
}
Листинг А.8. Приветствие (hi.cpp)
char Name[80];
cout << "Добрый день, " << Name << "!" << endl;
char Copy[80];
int i;
460
Язык C++ для тех, кто торопится
{
Copy[i] = Name[i];
}
Copy[i] = 0;
461
Приложение А
А.4. Функции
С помощью функции можно объединить несколько команд так,
чтобы их можно было запускать из любого места программы одной ко-
мандой. При этом большие программы становится легче прочесть. Если
в функции находится последовательность команд, которые часто требу-
ется повторять, то так программа становится еще и короче.
#include <iostream>
using namespace std;
char Name[80];
char Copy[80];
void InputName()
{
cout << "Как Вас зовут?" << endl;
cin.getline(Name, 80);
cout << "Добрый день, " << Name << "!" << endl;
}
void do_copy()
{
int i;
for (i=0; Name[i] && i<79; i++)
{
Copy[i] = Name[i];
}
Copy[i] = 0;
}
void show_copy()
{
462
Язык C++ для тех, кто торопится
cout << "Это Ваша копия: " << Copy << endl;
}
int main()
{
InputName();
do_copy();
show_copy();
}
Листинг А.9. Приветствие разделено на функции (fhi.cpp)
1
Это слово можно перевести как «пустой» или «не занятый».
463
Приложение А
void do_copy()
{
int i;
for (i=0; Name[i] && i<79; i++)
{
Copy[i] = Name[i];
}
Copy[i] = 0;
}
1 Конечно, это проблематичнее, чем прямо на месте произвести деление. Но если быть
последовательным, то чтобы разделить два числа, вам не требуется и писать программу на
C++. Вы наверняка просто воспользуетесь калькулятором или листом бумаги и ручкой.
465
Приложение А
А.4.3. Параметры
466
Язык C++ для тех, кто торопится
{
float quotient;
myfloat=26.8;
quotient = Div(myfloat, 4);
}
Листинг А.11. Передача параметров
Передача адреса
467
Приложение А
468
Язык C++ для тех, кто торопится
469
Приложение А
А.5. Классы
Большинство объектов в мире являются составными, поэтому их
нельзя поместить в переменную типа числа с плавающей запятой или
в массив. Человек, с точки зрения программы, имеет имя, адрес, дату рож-
дения и доход. Автомобиль состоит, к примеру, из марки, типа, мощности
и цены. Для определения таких объектов требуется объединить в одной
переменной несколько типов. Для этого в языке C++ используются клас-
сы. Следующий пример демонстрирует класс для автомобиля.
class Auto
{
public:
char brand [20];
char typ[30];
int kW;
float prise;
};
Листинг А.14. Класс для автомобиля
470
Язык C++ для тех, кто торопится
471
Приложение А
(см. стр. 32). Часто перед именем класса указывается буква t, c или C,
чтобы тем самым указать, что речь идет о классе, а не о переменной.
С помощью открытых фигурных скобок начинается определение эле-
ментов класса. Метка public: задает, что все дальнейшие определения
открыты. Эта тема будет подробнее рассмотрена далее.
char Name[MaxBez];
char BarCode[MaxCode];
float price;
};
tGoods chocolate;
chocolate.price = 0.70;
472
Язык C++ для тех, кто торопится
class tGoods
{
public:
char Name[MaxBez];
char BarCode[MaxCode];
void NewPrice(float new_price);
private:
float price;
};
void tGoods::NewPrice(float new_price)
{
Protocol(price, new_price);
price = new_price;
}
int main()
{
tGoods chocolate;
chocolate.NewPrice(0.70);
}
Листинг А.16. Изменение стоимости
473
Приложение А
{
Protocol(price, new_price);
price = new_price;
}
А.5.1. Конструктор
474
Язык C++ для тех, кто торопится
class tGoods
{
public:
tGoods(); // объявление конструктора
char Name[MaxBez];
char BarCode[MaxCode];
void NewPrice(float new_price);
float stock;
private:
float price;
};
tGoods:: tGoods()
{
Name[0] = 0;
BarCode[0] = 0;
price = 0.0;
stock = 0.0;
}
void tGoods::NewPrice(float new_price)
{
Protocol(price, new_price);
price = new_price;
}
int main()
{
tGoods chocolate;
chocolate.NewPrice(0.70);
}
Листинг А.17. Конструктор
475
Приложение А
Конструкторы с параметрами
Конструкторы могут снабжаться параметрами. Тогда объект уже
при создании будет инициализирован с определенными параметрами.
Они перечисляются в скобках при создании переменной.
476
Язык C++ для тех, кто торопится
class tGoods
{
public:
tGoods();
tGoods(char *Nm, char *Cd);
char Name[MaxBez];
char BarCode[MaxCode];
void NewPrice(float new_price);
float stock;
private:
float price;
};
tGoods:: tGoods()
{
Name[0] = 0;
BarCode[0] = 0;
price = 0.0;
stock = 0.0;
}
tGoods:: tGoods(char *Nm, char *Cd)
{
strcpy(Name, Nm);
strcpy(BarCode, Cd);
price = 0.0;
stock = 0.0;
}
void tGoods::NewPrice(float new_price)
{
Protocol(price, new_price);
price = new_price;
477
Приложение А
}
int main()
{
tGoods chocolate("Milk_chocolate", "4008400404127");
chocolate.NewPrice(0.70);
}
Листинг А.18. Изменение цены
478
Язык C++ для тех, кто торопится
А.5.2. Наследование
479
Приложение А
А.5.3. Полиморфизм
481
Приложение А
class tGoods
{
public:
...
virtual float InventPrice();
...
};
Листинг А.20. Виртуальная функция
tGoods * article[max_article];
int number_of_articles;
for (int i=0; i<number_of_articles; i++)
{
Summ += article[i]->stock * article[i]->InventPrice();
}
Листинг А.21. Инвентаризация
482
Язык C++ для тех, кто торопится
А.6. Шаблоны
С шаблонами мы переходим к так называемому обобщенному про-
граммированию. Его можно обозначить в качестве подхода, противопо-
ложного объектно ориентированному программированию, поскольку
здесь описываются алгоритмы, не зависящие от типов данных.
483
Приложение А
484
Язык C++ для тех, кто торопится
int i;
T Summ=0;
Эта функция работает с double или int. Она может также вызы-
ваться для любого класса, для которого определено сложение, деление
и присваивание, поскольку данные операции производятся над пере-
менными типа Т внутри функции. В языке C++ можно определить та-
кие операторы для собственного класса. Как это делается, рассмотрено
на стр. 238.
Б.1. KDevelop
Разработчики программ в среде Linux или UNIX с графической обо-
лочкой KDE1 могут использовать мощную среду KDevelop, которая име-
ет в своем распоряжении GNU-компилятор. Современные дистрибути-
вы Linux больше не исходят из того, что по другую сторону клавиатуры
находится программист, поэтому KDevelop требуется устанавливать
отдельно. Тем не менее вы найдете KDevelop на диске с дистрибутивом
вашей операционной системы Linux, его не требуется дополнительно
покупать или загружать из Всемирной паутины. Установка посредством
мастера инсталляции не сложнее установки редактора кода.
После запуска KDevelop похож внешним видом на обычную среду раз-
работки (IDE). Самое большое окно справа вверху предназначено для вво-
да программного кода, при этом различные элементы языка выделяются
различными цветами. В левой части расположено обзорное окно, в кото-
ром отображаются используемые в проекте классы. Под окном редактора
находится многофункциональное окно, в котором, например, могут выво-
диться как сообщения от компилятора, так и информация отладчика.
486
Устройство компилятора
487
Приложение Б
чик можно запустить также через главное меню. После его запуска мож-
но просмотреть выполнение программы в пошаговом режиме.
Б.2.1. Установка
1
Организованнее было бы конечно установить ее в каталог Programms.
489
Приложение Б
490
Устройство компилятора
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
system("PAUSE");
return EXIT_SUCCESS;
}
Листинг Б.1. Пустой консольный проект
491
Приложение Б
492
Устройство компилятора
Б.3. Cygwin
Cygwin — это не просто компилятор, а полноценная среда UNIX для
операционной системыWindows. Вы найдете программу на диске, при-
лагающемся к книге, в каталоге Программы/Cygwin. Запустите файл
setup.exe.
493
Приложение Б
Контрольные вопросы
Вопрос со стр. 23.
Функция ggt()
Задание со стр. 469.
#include <iostream>
using namespace std;
int ggt(int a, int b)
{
int help;
while (b>0)
{
if (b>a)
{
// обменять a и b
495
Приложение В
help = a;
a = b;
b = help;
}
a = a - b;
}
return a;
}
void shorten(int *numerator, int *denominator)
{
int factor;
factor = ggt(*numerator, *denominator);
*numerator = *numerator/ factor;
*denominator = *denominator/ factor;
}
int main()
{
int z=14;
int n=6;
shorten(&z, &n);
cout << z << " " << n << endl;
}
Листинг В.1. Сокращение с ggt()
Программа НДС
Задание со стр. 69.
#include <iostream>
using namespace std;
const float MwSt = 16;
int main()
{
float Netto, Tax, Brutto;
496
Примеры решений задач
#include <iostream>
using namespace std;
const float MwSt = 16;
int main()
{
float Netto, Tax, Brutto;
cout << "Введите цену нетто!" << endl;
cin >> Netto;
if (Netto > 0)
{
Tax = Netto * MwSt / 100;
cout << "Налог: " << Tax << endl;
Brutto = Netto + Tax;
cout << "Брутто: " << Brutto << endl;
}
else
{
cout << "Только положительные величины!" << endl;
}
}
Листинг В.3. Программа MwSt с проверкой ввода (ifmwst.cpp)
497
Приложение В
Сложный процент
Задание со стр. 114.
#include <iostream>
#include <iomanip.h>
using namespace std;
const int YearNow = 2003;
const int Years = 20;
const float Interest = 5;
const float Payment = 5000;
int main()
{
float Capital = Payment;
for (int i=0; i<Years; i++)
{
cout << setw(4) << i+YearNow << " - "
<< setw(12) << Capital << endl;
Capital += Capital * Interest / 100 + Payment;
}
}
Листинг В.4. Программа сложного процента (payment.cpp)
Угадывание чисел
Задание со стр. 114.
Первая часть задания состоит в том, чтобы найти случайное число
в диапазоне от 1 до 1000. Программа должна повторяться до тех пор,
пока пользователь не угадает это число. Поскольку ввод числа пользо-
вателем находится в середине тела цикла, проверка должна находиться
в его конце. Здесь предлагается цикл do-while. Внутри него запраши-
вается число. Чтобы выдать результирующее сообщение, проверяется
больше ли введенное число задуманного. Затем проверяется, меньше ли
оно. Проверка на равенство уже избыточна, поскольку тогда цикл будет
покинут. Церемония награждения располагается в конце программы.
Тот, кто там оказался — угадал число.
498
Примеры решений задач
#include <iostream>
using namespace std;
#include <stdlib.h>
int main()
{
int Get;
int SearchNumber;
srand(4);
SearchNumber = rand() % 1000 + 1;
do
{
cout << "Число от 1 до 1000!" << endl;
cin >> Get;
if (Get < SearchNumber)
{
cout << "Слишком маленькое!" << endl;
}
if (Get > SearchNumber)
{
cout << "Слишком большoе!" << endl;
}
}
while (Get != SearchNumber);
cout << "Угадал!" << endl;
}
Листинг В.5. Программа угадывания числа (guess.cpp)
#include <iostream>
using namespace std;
#include <stdlib.h>
499
Приложение В
500
Примеры решений задач
help = lotto[j];
lotto[j] = lotto[j+1];
lotto[j+1] = help;
}
}
}
// вывод отсортированных чисел
for (i=0; i<BallNumber; i++)
{
cout << lotto[i] << " ";
}
cout << endl;
}
Листинг В.6. Сортировка чисел лото (sortlotto.cpp)
#include <iostream>
using namespace std;
#include <stdlib.h>
const int MAX=12;
int main()
{
int field[MAX], help;
int i, j;
srand(56);
for(i=0; i<MAX; i++)
{
field[i] = rand() % 100 + 1;
}
bool sort = false;
501
Приложение В
Ввод дроби
Задание со стр. 131.
#include <iostream>
using namespace std;
const int MAX=256;
int main()
{
char input[MAX];
int i=0;
502
Примеры решений задач
double Value = 0;
double denominator = 0;
cin.getline(input, MAX);
// прочесть знаменатель
while (input[i]>='0' && input[i]<= '9')
{
Value *= 10;
Value += input[i] - '0';
i++;
}
// Если появляется символ /, значит имеется знаменатель
if (input[i]== '/')
{
// прочесть знаменатель
i++;
while (input[i]>= '0' && input[i]<= '9')
{
denominator *= 10;
denominator += input[i] - '0';
i++;
}
}
// разделить на знаменатель, если он был найден
if (denominator!=0)
{
Value /= denominator;
}
cout << Value << endl;
}
Листинг В.8. Ввод дроби (infraction.cpp)
Функция swap
Задание со стр. 175.
Напишите функцию swap(), которая получает два параметра по
ссылке и обменивает их содержимое.
503
Приложение В
Локальный стек
Задание со стр. 177.
Расширьте программу для реализации стека так, чтобы можно было
использовать не один, а любое количество стеков в программе, в т.ч.
и локальных.
#include <iostream>
using namespace std;
struct tListKnots
{
int data;
tListKnots *next;
};
void push(tListKnots **Anchor, int data)
{
504
Примеры решений задач
Вероятно, вас немного смущает, что параметр Anchor имеет два сим-
вола астериска. Причина в том, что Anchor изменяется внутри функции.
Чтобы изменения затронули также и переменную вызываемой функции,
ее нужно передавать либо через указатель, либо через ссылку.
505
Приложение В
506
Примеры решений задач
if (hier_is_a_ship(Ship, x, y, true))
{
return MaxShip;
}
else
{
for (int i=-1; i<=1; i++)
{
for (int j=-1; j<=1; j++)
{
if (!(i==0 && j==0))
{
Number += search(Ship, x, y, i, j);
}
}
}
}
return Number;
}
Листинг В.12. Поиск кораблей
507
Приложение В
void read_value()
{
while (*srcPos>='0' && *srcPos<'9')
{
TokenValue *= 10;
TokenValue += *srcPos-'0';
srcPos++;
}
if (*srcPos==',' ||*srcPos=='.')
{
double NK = 1;
srcPos++;
while (*srcPos>='0' && *srcPos<='9')
{
508
Примеры решений задач
NK *= 10;
TokenValue += (*srcPos-'0')/NK;
srcPos++;
}
}
}
tToken search_token()
// производит поиск лексемы с текущей позиции
// здесь также устанавливаются и записываются в
// глобальные переменные TokenValue числовые константы
{
aktToken = ERROR;
// пропустить пробел
while (*srcPos==' ')
srcPos++;
if (*srcPos==0)
{
aktToken = END;
}
else
{
switch (*srcPos)
{
case '(': aktToken=LPAR; break;
case ')': aktToken=RPAR; break;
case '*': aktToken=MUL; break;
case '/': aktToken=DIV; break;
case '+': aktToken=PLUS; break;
case '-': aktToken=MINUS; break;
}
if (*srcPos>='0' && *srcPos<'9')
{
aktToken=NUMBER;
TokenValue = 0.0;
read_value();
509
Приложение В
}
if (aktToken != NUMBER)
{
srcPos++;
}
}
return aktToken;
}
Листинг В.14. Распознавание чисел с плавающей запятой в калькуляторе
(floatcalc.cpp)
510
Примеры решений задач
Буфер: FIFO
Задание со стр. 231.
#include <limits.h>
#include <iostream>
using namespace std;
class tNode
{
public:
int d;
tNode *next;
};
class tFifo
{
public:
tFifo();
~tFifo();
void push(int);
int pop();
private:
tNode * Head;
tNode * Tail;
};
tFifo::tFifo()
{
Head = Tail = 0;
}
tFifo::~tFifo()
{
tNode *last = Head;
while (Head)
{
last = Head;
Head = Head->next;
511
Приложение В
delete last;
}
}
void tFifo::push(int d)
{
tNode *node = new tNode;
node->d = d;
node->next = 0;
// добавить в конец
if (Tail)
{
Tail->next = node;
}
if (Head==0)
{
Head = node;
}
Tail = node;
}
int tFifo::pop()
{
int content=-1;
if (Tail==Head) Tail = 0;
if (Head)
{
tNode *old = Head;
Head = Head->next;
content = old->d;
delete old;
}
return content;
}
int main()
{
512
Примеры решений задач
tFifo fifo;
fifo.push(2);
fifo.push(5);
fifo.push(18);
cout << fifo.pop() << endl;
cout << fifo.pop() << endl;
cout << fifo.pop() << endl;
}
Листинг В.15. Реализация FIFO
#include <iostream>
using namespace std;
template<class T> class tNode
{
public:
T d;
tNode<T> *next;
};
template<class T> class Stack
{
public:
Stack() { Anchor = 0; }
~Stack();
bool pop(T *);
bool push(T );
private:
513
Приложение В
514
Примеры решений задач
int main()
{
Stack<int> iStack;
int a;
iStack.push(8);
iStack.push(4);
iStack.push(2);
for (int i=0; i<3; i++)
{
if (iStack.pop(&a))
{
cout << a << endl;
}
}
}
Листинг В.16. Пример стека в виде списка (templstack.cpp)
#include <iostream>
using namespace std;
#include <string.h>
#include <stdlib.h>
double atof(const char *NumberString, char DecimalSign)
{
const int MAX=80;
char Buffer[MAX];
strncpy(Buffer, NumberString, MAX);
515
Приложение В
Buffer[MAX-1] = 0;
char *KommaPos = 0;
char KommaStr[2] = "A";
KommaStr[0] = DecimalSign;
KommaPos = strstr(Buffer, KommaStr);
if (KommaPos)
{
*KommaPos = '.';
}
return atof(Buffer);
}
int main(int argc, char **argv)
{
if (argc>1)
{
cout << atof(argv[1], ',') << endl;
}
}
Листинг В.17. Функция atof() с двумя параметрами
516
Приложение Г
ГЛОССАРИЙ
ANSI
CPU
GNU
517
Приложение Г
IDE
POSIX
RAD
Алгоритм
Аргумент
Ассемблера язык
Атрибут
Библиотека
Выражение
Выражение возвращает пригодный для использования результат. В са-
мом простом случае выражение является переменной или константой, но
это также может быть функция с возвращаемым значением или расчет.
Заголовочный файл
Заголовочный файл содержит определение для исходного кода про-
граммы или для объекта данных. Он подключается к исходному файлу
программы командой #include. Так можно использовать функции фай-
ла объекта.
Идентификатор
Понятие «идентификатор» происходит от английского термина
«handle», что означает «рукоятка» или «ручка». Например, при открытии
файла мы получаем идентификатор, по которому операционная система
может определить, какой файл программа «держит в руках». В большин-
стве случаев не имеет смысла рассматривать содержимое идентифика-
тора, поскольку оно имеет ценность только для операционной системы,
например, в качестве индекса внутренней структуры данных.
Команда
Команда — это законченное описание действия языка программирова-
ния. В языках C и C++ команда всегда заканчивается точкой с запятой.
519
Приложение Г
Компилятор
Компилятор переводит исходный код, написанный программистом,
в машинный. Из файла исходного кода создается файл объекта. Часто
компилятором называют комбинацию из компилятора и компоновщи-
ка. См. стр. 16.
Компоновщик
Компоновщик связывает несколько файлов объектов и библиотек
с программой. См. стр. 16.
Листинг
Листинг — распечатанный код программы.
Макрос
Техника обобщенного программирования, в языке C реализуемая
с помощью препроцессора и команды #include. В языке C++ с этой це-
лью используются шаблоны.
Метод
Функции класса в литературе по объектно ориентированному про-
граммированию называются методами. Это обозначение производит
правильное впечатление, что функции доступны только через объект
и поэтому являются действием объекта.
Модульное программирование
Концепция модульного программирования состоит в том, чтобы
объединить данные и функции в модули так, чтобы они могли исполь-
зоваться только через конкретный интерфейс. При этом работает прин-
цип скрытности. Наружу выдается ровно столько, сколько требуется
программисту других модулей для использования этого конкретного
модуля. Все остальное является деталями реализации, которые могут
быть в любой момент изменены автором модуля без информирования
об этом его коллег.
Обобщенное программирование
В обобщенном программировании создаются программные части,
которые можно использовать для нескольких различных типов данных.
В языке C для этого используются макросы, а в языке C++ — шаблоны.
520
Глоссарий
Объявление
ООП
Определение
Переменная
Постфикс
См. префикс.
Препроцессор
521
Приложение Г
Префикс
Производительность
Процедура
Процедурное программирование
Процесс
Рекурсия
522
Глоссарий
Синтаксис
Стек
Структурное программирование
Файл объекта
• Barclay Ken, Savage John. Object Oriented Design With C++. Prentice
Hall, 1996
• Borrmann Alf, Komnick Stefan, Landgrebe Gunnar, Matérne Jan,
Rätzmann Manfred, Sauer Jörg. Rational Rose und UML. Galileo Press,
2001.
• Dal Cin Mario, Lutz Joachim, Risse Thomas. Programmierung
in Modula-2. Teubner, 1986.
• Dal Cin Mario. Grundlagen der systemnahen Programmierung. Teubner,
1988.
• Jakobs Holger. Einführung in ISO C++. PDF. Загрузка:
www.c-plusplus.de
• Kaiser Richard. C++ mit dem Borland C++ Builder. Springer, 2002.
• Kaiser Ulrich, Kecher Christoph. C/C++ — Das umfassende Lehrbuch.
Galileo Press, 2006.
• Oestereich Bernd. Objektorientierte Softwareentwicklung.Oldenburg,
1999.
• Wigard Susanne. Visual C++ 6. bhv, 1999.
• Willemer Arnold. UNIX — Das umfassende Handbuch. Galileo Press,
2007.
• Willms André. C++ STL. Galileo Press, 2000.
• Wirth Niklaus. Algorithmen und Datenstrukturen mit Modula-2.
Teubner, 1986.
• Wolf Jürgen. C++ von A bis Z. Galileo Press, 2006.
• Брайан Керниган, Деннис Ритчи. Язык программирования C. М.:
Вильямс, 2012.
• Бьерн Страуструп. Язык программирования C++. М.: Бином, 2011.
524
Литература
526
Предметный указатель
Àðíîëüä Âèëëåìåð
ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ ÍÀ Ñ++
(îðûñ òiëiíäå)