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

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

Оператор

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

const int implausible_min = –200;

const int implausible_max = 200;

bool is_valid(const Reading& r)

// грубая проверка

{

if (r.day<1 || 31<r.day) return false;

if (r.hour<0 || 23<r.hour) return false;

if (r.temperature<implausible_min ||
implausible_max<r.temperature)

return false;

return true;

}

В

заключение можем прочитать объекты класса
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>
, добавив к нему функцию инициализации и просмотра.

vector<string> month_input_tbl; // month_input_tbl[0]=="jan"

void init_input_tbl(vector<string>& tbl)

// инициализирует вектор входных представлений

{

tbl.push_back("jan");

tbl.push_back("feb");

tbl.push_back("mar");

tbl.push_back("apr");

tbl.push_back("may");

tbl.push_back("jun");

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