7.1.3. Определение функций, не являющихся членом класса, но связанных с ним
Авторы классов нередко определяют такие вспомогательные функции, как наши функции add(), read() и print(). Хотя определяемые ими операции концептуально являются частью интерфейса класса, частью самого класса они не являются.
Мы определяем функции, не являющиеся членом класса, как любую другую функцию, т.е. ее объявление обычно отделено от определения (см. раздел 6.1.2). Функции, концептуально являющиеся частью класса, но не определенные в нем, как правило, объявляются (но не определяются) в том же заголовке, что и сам класс. Таким образом, чтобы использовать любую часть интерфейса, пользователю достаточно включить только один файл.

Определение функций read() и print()
Функции read() и print() выполняют ту же задачу, что и код в разделе 2.6.2, поэтому и не удивительно, что тела этих функций очень похожи на код, представленный там:
// введенные транзакции содержат ISBN, количество проданных книг и
// цену книги
istream &read(istream &is, Sales_data &item) {
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
ostream &print(ostream &os, const Sales_data &item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
Функция read() читает данные из предоставленного потока в заданный объект. Функция print() выводит содержимое предоставленного объекта в заданный поток.
В этих функциях, однако, следует обратить внимание на два момента. Во- первых, обе функции получают ссылки на соответствующие объекты классов ввода и вывода. Классы ввода и вывода — это типы, не допускающие копирования, поэтому их передача возможна только по ссылке (см. раздел 6.2.2). Кроме того, чтение и запись в поток изменяют его, поэтому обе функции получают обычные ссылки, а не ссылки на константы.
Второй заслуживающий внимания момент: функция print() не выводит новую строку. Обычно функции вывода осуществляют минимальное форматирование. Таким образом, пользовательский код может сам решить, нужна ли новая строка.
Определение функции add()
Функция add() получает два объекта класса Sales_data и возвращает новый объект класса Sales_data, представляющий их сумму:
Sales_data add(const Sales_data &lhs, const Sales_data &rhs) {
Sales_data sum = lhs; // копирование переменных-членов из lhs в sum
sum.combine(rhs); // добавить переменные-члены rhs в sum
return sum;
}
В теле функции определяется новый объект класса Sales_data по имени sum, предназначенный для хранения суммы двух транзакций. Инициализируем объект sum копией объекта lhs. По умолчанию копирование объекта класса подразумевает копирование и членов этого объекта. После копирования у членов bookNo, units_sold и revenue объекта sum будут те же значения, что и у таковых объекта lhs. Затем происходит вызов функции combine(), суммирующей значения переменных-членов units_sold и revenue объектов rhs и sum в последний. По завершении возвращается копия объекта sum.
Упражнения раздела 7.1.3
Упражнение 7.6. Определите собственные версии функций add(), read() и print().
Упражнение 7.7. Перепишите программу обработки транзакций, написанной для упражнений в разделе 7.1.2, так, чтобы использовать эти новые функции.
Упражнение 7.8. Почему функция read() определяет свой параметр типа Sales_data как простую ссылку, а функция print() — как ссылку на константу?
Упражнение 7.9. Добавьте в код, написанный для упражнений в разделе 7.1.2, операции чтения и вывода объектов класса Person.
Упражнение 7.10. Что делает условие в следующем операторе if?
if (read(read(cin, data1), data2))