Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:
const int not_a_reading = –7777; // ниже абсолютного нуля
Аналогично, мы заметили, что часто в течение некоторых месяцев не производилось ни одного измерения, поэтому ввели понятие “пропущен месяц”, вместо того чтобы проверять пропуски для каждого дня.
const int not_a_month = –1;
Три основных класса принимают следующий вид:
struct Day {
vector<double> hour;
Day; // инициализируем массив hour значениями "нет данных"
};
Day::Day
: hour(24)
{
for (int i = 0; i<hour.size; ++i) hour[i]=not_a_reading;
}
struct Month { //
месяц
int month; // [0:11] январю соответствует 0
vector<Day> day; // [1:31] один вектор для всех данных по дням
Month // не больше 31 дня в месяце (day[0]
// не используется)
:month(not_a_month), day(32) { }
};
struct Year { // год состоит из месяцев
int year; // положительный == н.э.
vector<Month> month; // [0:11] январю соответствует 0
Year :month(12) { } // 12 месяцев в году
};
В принципе каждый класс — это просто вектор, а классы
Month
и Year
содержат идентифицирующие члены month
и year
соответственно.
24
, 32
и 12
). Как правило, мы пытаемся избегать таких литеральных констант в коде. Эти константы носят фундаментальный характер (количество месяцев в году изменяется редко) и в остальной части кода не используются. Однако мы оставили их в коде в основном для того, чтобы напомнить вам о проблеме “волшебных чисел”, хотя намного предпочтительнее использовать символьные константы (см. раздел 7.6.1). Использование числа 32
для обозначения количества дней в месяце определенно требует объяснений; в таком случае число 32
действительно становится “волшебным”. 10.11.2. Считывание структурированных значений
Класс
Reading
будет использован только для ввода данных, к тому же он намного проще остальных
struct Reading {
int day;
int hour;
double temperature;
};
istream& operator>>(istream& is, Reading& r)
// считываем показания температуры из потока is в объект r
// формат: (3 4 9.7)
// проверяем формат, но не корректность данных
{
char ch1;
if (is>>ch1 && ch1!='('){ // можно это превратить в объект типа
// Reading?
is.unget;
is.clear(ios_base::failbit);
return is;
}
char ch2;
int d;
int h;
double t;
is >> d >> h >> t >> ch2;
if (!is || ch2!=')') error("Плохая
запись"); // перепутанные
// показания
r.day = d;
r.hour = h;
r.temperature = t;
return is;
}
В принципе мы проверяем, правильно ли начинается формат. Если нет, то переводим файл в состояние
fail
и выходим. Это позволяет нам попытаться считать информацию как-то иначе. С другой стороны, если ошибка формата обнаруживается после считывания данных и нет реальных шансов на возобновление работы, то вызываем функцию error
. Операции ввода в классе
Month
почти такие же, за исключением того, что в нем вводится произвольное количество объектов класса Reading
, а не фиксированный набор значений (как делает оператор >>
в классе Reading
).
istream& operator>>(istream& is, Month& m)
// считываем объект класса Month из потока is в объект m
// формат: { month feb... }
{
char ch = 0;
if (is >> ch && ch!='{') {
is.unget;
is.clear(ios_base::failbit); // ошибка при вводе Month
return is;
}
string month_marker;
string mm;
is >> month_marker >> mm;
if (!is || month_marker!="month") error("Неверное начало Month");
m.month = month_to_int(mm);
Reading r;
int duplicates = 0;
int invalids = 0;
while (is >> r) {
if (is_valid(r)) {
if (m.day[r.day].hour[r.hour] != not_a_reading)
++duplicates;
m.day[r.day].hour[r.hour] = r.temperature;
}
else
++invalids;
}
if (invalids) error("Неверные показания в Month", invalids);
if (duplicates) error("Повторяющиеся показания в Month",
duplicates);
end_of_loop(is,'}',"Неправильный конец Month");
return is;
}
Позднее мы еще вернемся к функции
month_to_int;
она преобразовывает символические обозначения месяцев, такие как jun
, в число из диапазона [0:11]
. Обратите внимание на использование функции end_of_loop
из раздела 10.10 для проверки признака завершения ввода. Мы подсчитываем количество неправильных и повторяющихся объектов класса Readings
(эта информация может кому-нибудь понадобиться).
Поделиться с друзьями: