4. Посмотрите на головоломный пример из раздела 8.4. Вставьте его в программу и объясните смысл каждой конструкции. Обратите внимание на то, что этот код не делает никаких осмысленных операций; он используется только для усложнения примера.
5. Для выполнения этого и нескольких следующих упражнений необходимо разработать и реализовать класс
Book
, который является частью программного обеспечения библиотеки. Класс
Book
должен иметь члены для хранения кода ISBN, названия, фамилии автора и даты регистрации авторских прав. Кроме того, он должен хранить данные о том, выдана книга на руки или нет. Создайте функции, возвращающие эти данные.
Создайте функции, проверяющие, выдана ли книга на руки или нет. Предусмотрите простую проверку данных, которые вводятся в объект класса
Book;
например, код ISBN допускается только в форме
n-n-n-x
, где
n
— целое число;
x
— цифра или буква.
6. Добавьте операторы в класс
Book
. Пусть оператор
==
проверяет, совпадают ли коды ISBN у двух книг. Пусть также оператор
!=
сравнивает цифры ISBN, а оператор
<<
выводит на печать название, фамилию автора и код ISBN в отдельных строках.
7. Создайте перечисление для класса
Book
с именем
Genre
. Предусмотрите типы для фантастики, прозы, периодических изданий, биографии и детской литературы. Отнесите каждую книгу к определенному жанру 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. Прочитать и записать их потоки.
Практические детали передачи символов с устройства и на устройство находятся в компетенции библиотеки ввода-вывода и драйверов устройств. В этой и следующей главах мы увидим, как создать систему ввода-вывода, состоящую из потоков форматированных данных, с помощью стандартной библиотеки языка С++.