1. Модифицируйте программу-калькулятор из главы 7, чтобы поток ввода стал явным параметром (как показано в разделе 8.5.8). Кроме того, напишите конструктор класса
Token_stream
и создайте параметр типа
istream&
, так, чтобы, когда вы поймете, как создать свои собственные потоки ввода и вывода (например, с помощью файлов), смогли использовать калькулятор, использующий их.
2. Напишите функцию
print
, которая выводит в поток
cout
вектор целых чисел. Пусть у нее будет два аргумента: строка для комментария результатов и объект класса
vector
.
3. Создайте вектор чисел Фибоначчи и выведите
их на печать с помощью функции из упр. 2. Для того чтобы создать вектор, напишите функцию
fibonacci(x,y,v,n)
, в которой аргументы
x
и
y
имеют тип
int
, аргумент
v
является пустой переменной типа
vector<int>
, а аргумент
n
— это количество элементов, подлежащих записи в вектор
v;
элемент
v[0]
равен
x
, а
v[1]
—
y
. Число Фибоначчи — это элемент последовательности, в которой каждый элемент является суммой двух предыдущих. Например, последовательность начинается с чисел 1 и 2, за ними следуют числа 1, 2, 3, 5, 8, 13, 21... Функция
fibonacci
должна генерировать такую последовательность, начинающуюся с чисел
x
и
y
.
4. Переменная типа
int
может хранить целые числа, не превышающие некоторого максимального числа. Вычислите приближение этого максимального числа с помощью функции
fibonacci
.
5. Напишите две функции, изменяющие порядок следования элементов в объекте типа
vector<int>
. Например, вектор 1, 3, 5, 7, 9 становится вектором 9, 7, 5, 3, 1. Первая функция, изменяющая порядок следования элементов на противоположный, должна создавать новый объект класса
vector
, а исходный объект класса
vector
должен оставаться неизменным. Другая функция должна изменять порядок следования элементов без использования других векторов. (Подсказка: как функция
swap
.)
6. Напишите варианты функций из упражнения 5 для класса
vector<string>
.
7. Запишите пять имен в вектор
vector<string> name
, затем предложите пользователю указать возраст названных людей и запишите их в вектор
vector<double> age
. Затем выведите на печать пять пар
(name[i],age[i])
. Упорядочьте имена
(sort(name.begin, name.end))
и выведите на печать пары
(name[i], age[i])
. Сложность здесь заключается в том, чтобы получить вектор
age
, в котором порядок следования элементов соответствовал бы порядку следования элементов вектора
name
. (Подсказка: перед сортировкой вектора
name
создайте его копию и используйте ее для получения упорядоченного вектора
age
. Затем выполните упражнение снова, разрешив использование произвольного количества имен).
8. Напишите простую функцию
randint
, генерирующую псевдослучайные числа в диапазоне
[0:MAXINT]
. (Подсказка: Д. Кнут Искусство программирования, том 2.)
9. Напишите функцию, которая с помощью функции
randint
из предыдущего упражнения вычисляет псевдослучайное целое число в диапазоне [a:b]:
rand_in_range(int a, int b)
. Примечание: эта функция очень полезна для создания простых игр.
10. Напишите функцию, которая по двум объектам,
price
и
weight
, класса
vector<double>
вычисляет значение (“индекс”), равное сумме всех произведений
price[i]*weight[i]
. Заметьте, что должно выполняться условие
weight.size<=price.size
.
11. Напишите функцию
maxv
, возвращающую наибольший элемент вектора.
12. Напишите функцию, которая находит наименьший и наибольший элементы вектора, являющегося ее аргументом, а также вычисляющую их среднее и медиану. Результаты можно вернуть либо в виде структуры
struct
, либо с помощью механизма передачи аргументов по ссылке. Какой из этих двух способов следует предпочесть и почему?
13. Усовершенствуйте функцию
print_until_s
из раздела 8.5.2. Протестируйте ее. Какие наборы данных лучше всего подходят для тестирования? Укажите причины. Затем напишите функцию
print_until_ss
, которая выводит на печать сроки, пока не обнаружит строку аргумента
quit
.
14. Напишите функцию, принимающую аргумент типа
vector<string>
и возвращающую объект типа
vector<int>
, содержащий количество символов в каждой строке. Кроме того, найдите самую короткую и самую длинную строки, а также первую и последнюю строки в соответствии с лексикографическим порядком Сколько отдельных функций вы использовали бы для решения этой задачи? Почему?
15. Можно ли объявить константный аргумент функции, который передается не по ссылке (например,
void f(const int);)
? Что это значит? Зачем это нужно? Почему эта конструкция применяется редко? Испытайте ее; напишите несколько маленьких программ, чтобы увидеть, как она работает.
Послесловие
Большую часть этой (и следующей) главы можно было бы вынести в приложение. Однако в части II нам потребуются многие средства, описанные здесь. Кроме того, очень скоро мы столкнемся с проблемами, для решения которых эти средства были изобретены. При написании простых программ вы неизбежно должны будете решать такие проблемы. Итак, для того чтобы сэкономить время и минимизировать недоразумения, необходим систематический подход, а не серия “случайных” ссылок на справочное руководство и приложения.
Глава 9. Технические детали: классы и прочее
“Помните, все требует времени”.
Пит Хейн (Piet Hein)
В этой главе мы сосредоточим внимание на основном инструменте программирования: языке С++. Мы опишем технические подробности этого языка, связанные в основном с типами, определенными пользователем, иначе говоря, с классами и перечислениями. Описание свойств языка излагается на примере постепенной разработки типа
Date
. Кроме того, это позволяет продемонстрировать некоторые полезные приемы разработки классов.
9.1. Типы, определенные пользователем
В языке С++ есть встроенные типы, такие как
char
,
int
и
double
(подробнее они описаны в разделе A.8). Тип называется встроенным, если компилятор знает, как представить объекты такого типа и какие операторы к нему можно применять (такие как
+
и
–
) без уточнений в виде объявлений, которые создает программист в исходном коде.