Читаем Программирование полностью

  Year() :month(12) { } // 12 месяцев в году

};

В принципе каждый класс — это просто вектор, а классы Month и Year содержат идентифицирующие члены month и year соответственно.

  В этом примере существует несколько “волшебных констант” (например, 24, 32 и 12). Как правило, мы пытаемся избегать таких литеральных констант в коде. Эти константы носят фундаментальный характер (количество месяцев в году изменяется редко) и в остальной части кода не используются. Однако мы оставили их в коде в основном для того, чтобы напомнить вам о проблеме “волшебных чисел”, хотя намного предпочтительнее использовать символьные константы (см. раздел 7.6.1). Использование числа 32 для обозначения количества дней в месяце определенно требует объяснений; в таком случае число 32 действительно становится “волшебным”. 

<p id="AutBody_Root190"><strong>10.11.2. Считывание структурированных значений</strong></p>

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

Оператор >> в классе Month выполняет грубую проверку корректности объекта класса Reading, прежде чем записать его в память.

const int implausible_min = –200;

const int implausible_max = 200;

bool is_valid(const Reading& r)

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже