Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования 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
(эта информация может кому-нибудь понадобиться).

Поделиться с друзьями: