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

Можно было бы уточнить содержание блоков try и catch, но это внесет в программу еще большую путаницу. Ошибки в принципе трудно обрабатывать, а ошибки, возникающие при обработке других ошибок, обрабатывать еще труднее. Поэтому стоит попытаться найти способ удалять из потока Token_stream символы, которые могут породить исключение. Единственный путь для ввода данных в калькулятор пролегает через функцию get(), и он может, как мы только что выяснили, порождать исключения. Таким образом, необходима новая операция. Очевидно, что ее целесообразно поместить в класс Token_stream.

class Token_stream {

public:

  Token_stream(); // создает поток Token_stream, считывающий

                  // данные из потока cin

  Token get();    // считывает лексему

  void putback(Token t); // возвращает лексему

  void ignore(char c);   // отбрасывает символы,

                         // предшествующие символу с включительно

private:

  bool full;             // есть лексема в буфере?

  Token buffer; // здесь хранится лексема, которая возвращается

                // назад с помощью функции putback()

};

Функция ignore() должна быть членом класса Token_stream, так как она должна иметь доступ к его буферу. Мы выбрали в качестве искомого символа аргумент функции ignore(). Помимо всего прочего, объект класса Token_stream не обязан знать, что калькулятор считает хорошим символом для исправления ошибок. Мы решили, что этот аргумент должен быть символом, потому что не хотим рисковать, работая с составными лексемами (мы уже видели, что при этом происходит). Итак, мы получаем следующую функцию:

void Token_stream::ignore(char c)

  // символ c обозначает разновидность лексем

{

  // сначала проверяем буфер:

  if (full && c==buffer.kind) {

    full = false;

    return;

  }

  full = false;

  // теперь проверяем входные данные:

  char ch = 0;

  while (cin>>ch)

    if (ch==c) return;

}

В этом коде сначала происходит проверка буфера. Если в буфере есть символ c, прекращаем работу, отбрасывая этот символ c; в противном случае необходимо считывать символы из потока cin, пока не встретится символ c. Теперь функцию clean_up_mess() можно написать следующим образом:

void clean_up_mess()

{

  ts.ignore(print);

}

Обработка ошибок всегда является сложной. Она требует постоянного экспериментирования и тестирования, поскольку крайне трудно представить заранее, какая ошибка может возникнуть в ходе выполнения программы. Защита программы от неправильного использования всегда представляет собой очень сложную задачу. Дилетанты об этом никогда не беспокоятся. Качественная обработка ошибок — один из признаков профессионализма.

<p id="AutBody_Root127"><strong>7.8. Переменные</strong></p>

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

Аналогично для научных вычислений хотелось бы иметь встроенные имена, такие как pi и e, как в научных калькуляторах. Переменные и константы — основные новшества, которые мы внесем в калькулятор. Это коснется многих частей кода. Такие действия не следует предпринимать без весомых причин и без достаточного времени на работу. В данном случае мы вносим переменные и константы, поскольку это дает возможность еще раз проанализировать код и освоить новые методы программирования.

<p id="AutBody_Root128"><strong>7.8.1. Переменные и определения</strong></p>

 Очевидно, что для работы с переменными и константами программа-калькулятор должна хранить пары (имя, значение) так, чтобы мы имели доступ к значению по имени. Класс Variable можно определить следующим образом:

class Variable {

public:

  string name;

  double value;

  Variable (string n, double v) :name(n), value(v) { }

};

Член класса name используется для идентификации объекта класса Variable, а член value — для хранения значения, соответствующего члену name. Конструктор добавлен просто для удобства.

Как хранить объекты класса Variable так, чтобы их значение можно было найти или изменить по строке name? Оглядываясь назад, видим, что на этот вопрос есть только один правильный ответ: в виде вектора объектов класса Variable.

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