фантастики, прозы, периодических изданий, биографии и детской литературы. Отнесите каждую книгу к определенному жанру Genre и внесите соответствующие изменения в конструктор класса Book и его функции-члены.
8. Создайте класс Patron для библиотеки. Этот класс должен содержать имя пользователя, номер библиотечной карточки, а также размер членского взноса. Предусмотрите функции, имеющие доступ к этим членам, а также функцию, устанавливающую размер членского взноса. Предусмотрите вспомогательный метод, возвращающий булево значение (bool) в зависимости от того, заплатил пользователь членские взносы или нет.
9. Создайте класс Library. Включите в него векторы классов Book и Patron. Включите также структуру Transaction и предусмотрите в ней члены классов Book, Patron и Date. Создайте вектор объектов класса Transaction. Создайте функции, добавляющие записи о книгах и клиентах библиотеки, а также о состоянии книг. Если пользователь взял книгу, библиотека должна быть уверена, что пользователь является ее клиентом, а книга принадлежит ее фондам. Если эти условия не выполняются, выдайте сообщение об ошибке. Проверьте, есть ли у пользователя задолженность по уплате членских взносов. Если задолженность есть, выдайте сообщение об ошибке. Если нет, создайте объект класса Transaction и замените его в векторе объектов класса Transaction. Кроме того, создайте метод, возвращающий вектор, содержащий имена всех клиентов, имеющих задолженность.
10. Реализуйте функцию leapyear() из раздела 9.8.
11. Разработайте и реализуйте набор полезных вспомогательных функций для класса Date, включая такие функции, как next_workday() (в предположении, что любой день, кроме субботы и воскресенья, является рабочим) и week_of_year() (в предположении, что первая неделя начинается 1 января, а первый день недели — воскресенье).
12. Измените представление класса Date и пронумеруйте дни, прошедшие с 1 января 1970 года (так называемый нулевой день), с помощью переменной типа long и переработайте функции из раздела 9.8. Предусмотрите идентификацию дат, выходящих за пределы допустимого диапазона (отбрасывайте все даты, предшествующие нулевому дню, т.е. не допускайте отрицательных дней).
13. Разработайте и реализуйте класс для представления рациональных чисел Rational. Рациональное число состоит из двух частей: числителя и знаменателя, например 5/6 (пять шестых, или .83333). При необходимости еще раз проверьте определение класса. Предусмотрите операторы присваивания, сложения, вычитания, умножения, деления и проверки равенства. Кроме того, предусмотрите преобразование в тип double. Зачем нужен класс Rational?
14. Разработайте и реализуйте класс Money для вычислений, связанных с долларами и центами, точность которых определяется по правилу округления 4/5 (0,5 цента округляется вверх, все, что меньше 0,5, округляется вниз). Денежные суммы должны представляться в центах с помощью переменной типа long, но ввод и вывод должны использовать доллары и центы, например $123.45. Не беспокойтесь о суммах, выходящих за пределы диапазона типа long.
15. Уточните класс Money, добавив валюту (как аргумент конструктора). Начальное значение в виде десятичного числа допускается, поскольку такое число можно представить в виде переменной типа long. Не допускайте некорректных операций. Например, выражение Money*Money не имеет смысла, а USD1.23+DKK5.00 имеет смысл, только если существует таблица преобразования, определяющая обменный курс между американскими долларами (USD) и датскими кронами (DKK).
16. Приведите пример вычислений, в котором класс Rational позволяет получить более точные результаты, чем класс Money.
17. Приведите пример вычислений, в котором класс Rational позволяет получить более точные результаты, чем тип double.
Послесловие
Существует много типов, определенных пользователем. Их гораздо больше, чем представлено здесь. Типы, определенные пользователем, особенно классы, образуют ядро языка С++ и являются ключом ко многим эффективным методам проектирования. Большая часть оставшихся глав посвящена проектированию и использованию классов. Класс — или набор классов — это механизм, позволяющий выразить наши концепции в виде кода. В этой главе мы изложили в основном языковые аспекты классов, в последующих главах мы сосредоточимся на том, как элегантно выразить полезные идеи в виде классов.
Часть II
Ввод и вывод
Глава 10
Потоки ввода и вывода
“Наука — это знания о том, как не дать себя одурачить”.
Ричард Фейнман (Richard P. Feynman)
В этой и следующих главах описываются стандартные средства ввода и вывода в языке С++: потоки ввода-вывода. Показано, как читать и записывать файлы, как обрабатывать ошибки, а также применять операторы ввода-вывода к типам, определенным пользователем. В центре внимания данной главы находится базовая модель: как читать и записывать отдельные значения, как открывать, читать и записывать целые файлы. В заключительном примере приводится большой фрагмент кода, иллюстрирующий эти аспекты программирования. Детали описываются в следующей главе.
10.1. Ввод и вывод
Без данных вычисления бессмысленны. Для выполнения интересующих нас вычислений мы должны ввести в программу данные и получить результаты. В разделе 4.1 мы уже упоминали о чрезвычайном разнообразии источников данных и адресатов для вывода. Если мы не проявим осторожность, то будем писать программы, получающие входные данных только из конкретного источника и выдающие результаты только на конкретное устройство вывода. В определенных приложениях, например цифровых фотоаппаратах или сенсорах топливного инжектора, это может быть приемлемым (а иногда даже необходимым), но при решении задач более общего характера нам необходимо разделять способы, с помощью которых программа читает и записывает данные, от реальных устройств ввода и вывода. Если бы мы были вынуждены непосредственно обращаться к устройствам разных видов, то каждый раз, когда на рынке появляется новый экран или диск, должны были бы изменять свою программу или ограничивать пользователей лишь теми экранами и дисками, которые нам нравятся. Разумеется, это абсурд.
Большинство современных операционных систем поручают управление устройствами ввода-вывода специализированным драйверам, а затем программы обращаются к ним с помощью средств библиотеки ввода-вывода, обеспечивающих максимально единообразную связь с разными источниками и адресатами данных. В общем, драйверы устройств глубоко внедрены в операционную систему и недоступны для большинства пользователей, а библиотечные средства ввода-вывода обеспечивают абстракцию ввода-вывода, так что программист не должен думать об устройствах и их драйверах.
Когда используется такая модель, вся входная и выходная информация может рассматриваться как потоки байтов (символы), обрабатываемые средствами библиотеки ввода-вывода. Наша работа как программистов, создающих приложения, сводится к следующему.
1. Настроить потоки ввода-вывода на соответствующие источники и адресаты данных.
2. Прочитать и записать их потоки.
Практические детали передачи символов