std::cout << totalRevenue/totalCnt << std::endl;
else
std::cout << "(no sales)" << std::endl;
return 0; //
} else { //
std::cerr << "Data must refer to the same ISBN"
<< std::endl;
return -1; //
}
Первый оператор if
сравнивает члены bookNo
объектов data1
и data2
. Если эти члены содержат одинаковый ISBN, выполняется код в фигурных скобках, суммирующий компоненты двух переменных. Поскольку необходимо вывести среднюю цену, сначала вычислим общее количество проданных экземпляров и общий доход, а затем сохраним их в переменных totalCnt
и totalRevenue
соответственно. Выводим эти значения, а затем проверяем, были ли книги проданы, и если да, то выводим вычисленную среднюю цену за книгу. Если никаких продаж не было, выводим сообщение, обращающее внимание на этот факт.
Упражнение 2.41. Используйте класс Sales_data
для перезаписи кода упражнений из разделов 1.5.1, 1.5.2 и 1.6. А также определите свой класс Sales_data
в том же файле, что и функция main()
.
Как будет продемонстрировано в разделе 19.7, класс можно определить в функции, однако такие классы ограничены по функциональным возможностям. Поэтому классы обычно не определяют в функциях. При определении класса за пределами функции в каждом файле исходного кода может быть только одно определение этого класса. Кроме того, если класс используется в нескольких разных файлах, определение класса в каждом файле должно быть тем же.
Чтобы гарантировать совпадение определений класса в каждом файле, классы обычно определяют в файлах заголовка. Как правило, классы хранятся в заголовках, имя которых совпадает с именем класса. Например, библиотечный тип string
определен в заголовке string
. Точно так же, как уже было продемонстрировано, наш класс Sales_data
определен в файле заголовка Sales_data.h
.
Заголовки (обычно) содержат сущности (такие как определения класса или переменных const
и constexpr
(см. раздел 2.4), которые могут быть определены в любом файле только однажды. Однако заголовки нередко должны использовать средства из других заголовков. Например, поскольку у класса Sales_data
есть член типа string
, заголовок Sales_data.h
должен включать заголовок string
. Как уже упоминалось, программы, использующие класс Sales_data
, должны также включать заголовок string
, чтобы использовать член bookNo
. В результате использующие класс Sales_data
программы будут включать заголовок string
дважды: один раз непосредственно и один раз как следствие включения заголовка Sales_data.h
. Поскольку заголовок мог бы быть включен несколько раз, код необходимо писать так, чтобы обезопасить от многократного включения.
После внесения любых изменений в заголовок необходимо перекомпилировать все использующие его файлы исходного кода, чтобы вступили в силу новые или измененные объявления.
Наиболее распространенный способ обезопасить заголовок от многократного включения подразумевает использование препроцессора. #include
. Когда препроцессор встречает директиву #include
, он заменяет ее содержимым указанного заголовка.
Программы С++ используют также препроцессор для #define
получает имя и определяет его как переменную препроцессора. Есть еще две директивы, способные проверить, определена ли данная переменная препроцессора или нет. Директива #ifdef
истинна, если переменная была определена, а директива #ifndef
истинна, если переменная #ifdef
или #ifndef
и до соответствующей директивы #endif
.
Эти средства можно использовать для принятия мер против множественного включения следующим образом:
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
#endif