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). Тип называется встроенным, если компилятор знает, как представить объекты такого типа и какие операторы к нему можно применять (такие как
+
и
–
) без уточнений в виде объявлений, которые создает программист в исходном коде.
Типы, не относящиеся к встроенным, называют
типами, определенными пользователем (user-defined types — UDT). Они могут быть частью стандартной библиотеки, доступной в любой реализации языка С++ (например, классы
string
,
vector
и
ostream
, описанные в главе 10), или типами, самостоятельно созданными программистом, как классы
Token
и
Token_stream
(см. разделы 6.5 и 6.6). Как только мы освоим необходимые технические детали, мы создадим графические типы, такие как
Shape
,
Line
и
Text
(речь о них пойдет в главе 13). Стандартные библиотечные типы являются такой же частью языка, как и встроенные типы, но мы все же рассматриваем их как определенные пользователем, поскольку они созданы из таких же элементарных конструкций и с помощью тех же приемов, как и типы, разработанные нами; разработчики стандартных библиотек не имеют особых привилегий и средств, которых нет у нас. Как и встроенные типы, большинство типов, определенных пользователем, описывают операции. Например, класс
vector
содержит операции
[]
и
size()
(см. разделы 4.6.1 и В.4.8), класс
ostream
операцию
<<
, класс
Token_stream
операцию
get()
(см. раздел 6.8), а класс
Shape
операции
add(Point)
и
set_color()
(см. раздел 14.2).
Зачем мы создаем типы? Компилятор не знает всех типов, на основе которых мы хотим создавать свои программы. Это в принципе невозможно, поскольку существует слишком много полезных типов — ни один разработчик языка программирования или компиляторов не может знать обо всех. Каждый день мы разрабатываем новый тип. Почему? Какие типы можно признать хорошими? Типы являются хорошими, если они позволяют прямо отразить идею в коде. Когда мы пишем программу, нам хотелось бы непосредственно воплощать идеи в коде так, чтобы мы сами, наши коллеги и компилятор могли понять, что мы написали. Когда мы хотим выполнять арифметические операции над целыми числами, нам отлично подойдет тип
int;
когда хотим манипулировать текстом, класс
string
— хороший выбор; когда хотим манипулировать входной информацией для калькулятора, нам нужны классы
Token
и
Token_stream
. Необходимость этих классов имеет два аспекта.