Литмир - Электронная Библиотека
A
A

В заключение можем прочитать объекты класса

Year
. Оператор
>>
в классе
Year
аналогичен оператору
>>
в классе
Month
.

istream& operator>>(istream& is, Year& y)

  // считывает объект класса Year из потока is в объект y

  // формат: { year 1972... }

{

  char ch;

  is >> ch;

  if (ch!='{') {

    is.unget();

    is.clear(ios::failbit);

    return is;

  }

  string year_marker;

  int yy;

  is >> year_marker >> yy;

  if (!is || year_marker!="year")

    error("Неправильное начало Year");

  y.year = yy;

  while(true) {

    Month m; // каждый раз создаем новый объект m

    if(!(is >> m)) break;

    y.month[m.month] = m;

  }

  end_of_loop(is,'}',"Неправильный конец Year");

  return is;

}

Можно было бы сказать, что этот оператор “удручающе аналогичен”, а не просто аналогичен, но здесь кроется важный нюанс. Посмотрите на цикл чтения. Ожидали ли вы чего-нибудь подобного следующему фрагменту?

Month m;

while (is >> m)

y.month[m.month] = m;

Возможно, да, поскольку именно так мы до сих пор записывали все циклы ввода. Именно этот фрагмент мы написали первым, и он является неправильным. Проблема заключается в том, что функция

operator>>(istream& is, Month& m)
не присваивает объекту m совершенно новое значение; она просто добавляет в него данные из объектов класса
Reading
. Таким образом, повторяющаяся инструкция
is>>m
добавляла бы данные в один и тот же объект
m
. К сожалению, в этом случае каждый новый объект класса
Month
содержал бы все показания всех предшествующих месяцев текущего года. Для того чтобы считывать данные с помощью инструкции
is>>m
, нам нужен совершенно новый объект класса
Month
. Проще всего поместить определение объекта m в цикл так, чтобы он инициализировался на каждой итерации.

В качестве альтернативы можно было бы сделать так, чтобы функция

operator>>(istream& is, Month& m)
перед считыванием в цикле присваивала бы объекту
m
пустой объект.

Month m;

while (is >> m) {

  y.month[m.month] = m;

  m = Month(); // "Повторная инициализация" объекта m

}

Попробуем применить это.

// открываем файл для ввода:

cout << "Пожалуйста, введите имя файла для ввода \n";

string name;

cin >> name;

ifstream ifs(name.c_str());

if (!ifs) error(" невозможно открыть файл для ввода ",name);

ifs.exceptions(ifs.exceptions()|ios_base::badbit); // генерируем bad()

// открываем файл для вывода:

cout << "Пожалуйста, введите имя файла для ввода \n";

cin >> name;

ofstream ofs(name.c_str());

if (!ofs) error(" невозможно открыть файл для ввода ",name);

// считываем произвольное количество объектов класса Year:

vector<Year> ys;

while(true) {

  Year y; // объект класса Year каждый раз очищается

  if (!(ifs>>y)) break;

  ys.push_back(y);

}

cout << " считано " << ys.size() << " записей по годам.\n";

for (int i = 0; i<ys.size(); ++i) print_year(ofs,ys[i]);

Функцию

print_year()
мы оставляем в качестве упражнения. 

10.11.3. Изменение представления

Для того чтобы оператор

>>
класса
Month
работал, необходимо предусмотреть способ для ввода символьных представлений месяца. Для симметрии мы описываем способ сравнения с помощью символьного представления. Было бы слишком утомительно писать инструкции
if
, подобные следующей:

if (s=="jan")

  m = 1;

else if (s=="feb")

  m = 2;

...

Это не просто утомительно; таким образом мы встраиваем названия месяцев в код. Было бы лучше занести их в таблицу, чтобы основная программа оставалась неизменной, даже если мы изменим символьное представление месяцев. Мы решили представить входную информацию в виде класса

vector<string>
, добавив к нему функцию инициализации и просмотра.

151
{"b":"847443","o":1}