Приступим к разработке типа Token_stream. Что пользователь ждет от него? Очевидно, что нам нужны функции get() и putback() — именно поэтому мы ввели понятие потока лексем. Класс Token_stream должен создавать объекты класса Token из символов, считанных из потока ввода, поэтому нам необходима возможность создавать объекты класса Token_stream, способные считывать данные из потока cin. Таким образом, простейший вариант класса Token_stream выглядит примерно так:
class Token_stream {
public:
Token_stream(); // создает объект класса Token_stream,
// считывающий данные из потока cin
Token get(); // получает объект класса Token
void putback(Token t); // возвращает объект класса Token
// обратно
private:
// детали реализации
};
Это все, что требуется от пользователя для использования объектов класса Token_stream. Опытные программисты могут поинтересоваться, почему поток cin является единственным возможным источником символов, — просто мы решили вводить символы с клавиатуры. Это решение можно пересмотреть в упражнении, приведенном в главе 7.
Почему мы использовали “длинное” имя putback(), а не логичное имя put()? Тем самым мы подчеркнули асимметрию между функциями get() и putback(): мы возвращаем лексему в поток ввода, а не вставляем ее в поток вывода. Кроме того, функция putback() есть в классе istream: непротиворечивость имен — полезное свойство. Это позволяет людям запоминать имена функций и избегать ошибок.
Теперь можем создать класс Token_stream и использовать его.
Token_stream ts; // объект класса Token_stream с именем ts
Token t = ts.get(); // получаем следующий объект класса Token из объекта ts
// ...
ts.putback(t); // возвращает объект t класса Token обратно в объект ts
Это все, что нам нужно, чтобы закончить разработку калькулятора.
6.8.1. Реализация класса Token_stream
Теперь необходимо реализовать три функции класса Token_stream. Как представить класс Token_stream? Иначе говоря, какие данные необходимо хранить в объекте класса Token_stream, чтобы он мог выполнить свое задание? Необходима память для лексемы, которая будет возвращена обратно в объект класса Token_stream. Для простоты будем считать, что лексемы возвращаются в поток по одной. Этого вполне достаточно для нашей программы (а также для очень многих аналогичных программ). Таким образом, нужна память для одного объекта класса Token и индикатор ее занятости.
class Token_stream {
public:
Token_stream(); // создает объект класса Token_stream,
// считывающий данные из потока cin
Token get(); // получает объект класса Token
// (функция get() определена в разделе 6.8.2)
void putback(Token t); // возвращает объект класса Token
// обратно
private:
bool full; // находится ли в буфере объект класса Token?
Token buffer; // здесь хранится объект класса Token,
// возвращаемый в поток функцией putback()
};
Теперь можно определить (написать) три функции-члена. Конструктор и функция putback() никаких трудностей не вызывают, поскольку они невелики. Мы определим их в первую очередь. Конструктор просто устанавливает настройки, свидетельствующие о том, что буфер пуст.
Token_stream::Token_stream()
:full(false), buffer(0) // в буфере нет ни одного объекта