KnigkinDom.org» » »📕 Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп читаем онлайн бесплатно полную версию! Чтобы начать читать не надо регистрации. Напомним, что читать онлайн вы можете не только на компьютере, но и на андроид (Android), iPhone и iPad. Приятного чтения!

1 ... 74 75 76 77 78 79 80 81 82 ... 337
Перейти на страницу:

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
Первичное выражение

  + Первичное выражение

Число:

  литерал_с_плавающей_точкой

Ввод из потока cin через поток Token_stream с именем ts.

*/

Здесь мы использовали блок комментариев, который начинается символами /* и заканчивается символами */. В реальной программе история пересмотра может содержать сведения о том, какие именно изменения были внесены и какие улучшения были сделаны. Обратите внимание на то, что эти комментарии помещены за пределами кода. Фактически это несколько упрощенная грамматика: сравните правило для Инструкции с тем, что на самом деле происходит в программе (например, взгляните на код в следующем разделе). Этот комментарий ничего не говорит от цикле в функции calculate(), позволяющем выполнять несколько вычислений в рамках одного сеанса работы программы. Мы вернемся к этой проблеме в разделе 7.8.1.

7.7. Исправление ошибок

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

До сих пор все ошибки представлялись в виде исключений и обрабатывались функцией main(). Если мы хотим исправить ошибку, то функция calculate() должна перехватывать исключения и попытаться устранить неисправность прежде, чем приступить к вычислению следующего выражения.

void calculate()

{

  while (cin)

  try {

    cout << prompt;

    Token t = ts.get();

    while (t.kind == print) t=ts.get(); // сначала

                                        // игнорируем все

                                        // инструкции "печать"

    if (t.kind == quit) return;

    ts.putback(t);

    cout << result << expression() << endl;

  }

  catch (exception& e) {

    cerr << e.what() << endl; // выводим сообщение об ошибке

    clean_up_mess();

  }

}

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

Рассмотрим пример.

1++2*3; 4+5;

Эти выражения вызывают ошибку, и лексемы 2*3; 4+5 останутся в буферах потоков Token_stream и cin после того, как второй символ + породит исключение.

У нас есть две возможности.

1. Удалить все лексемы из потока Token_stream.

2. Удалить из потока все лексемы Token_stream, связанные с текущими вычислениями.

В первом случае отбрасываем все лексемы (включая 4+5;), а во втором — отбрасываем только лексему 2*3, оставляя лексему 4+5 для последующего вычисления. Один выбор является разумным, а второй может удивить пользователя. Обе альтернативы одинаково просто реализуются. Мы предпочли второй вариант, поскольку его проще протестировать. Он выглядит проще. Чтение лексем выполняется функцией get(), поэтому можно написать функцию clean_up_mess(), имеющую примерно такой вид:

void clean_up_mess() // наивно

{

  while (true) { // пропускаем,

                 // пока не обнаружим инструкцию "печать"

    Token t = ts.get();

    if (t.kind == print) return;

  }

}

К сожалению, эта функция не всегда работает хорошо. Почему? Рассмотрим следующий вариант:

1@z; 1+3;

Символ @ приводит нас к разделу catch в цикле while. Тогда для выявления следующей точки с запятой вызываем функцию clean_up_mess(). Функция clean_up_mess() вызывает функцию get() и считывает символ z. Это порождает следующую ошибку (поскольку символ z не является лексемой), и мы снова оказываемся в блоке catch внутри функции main() и выходим из программы. Ой! У нас теперь нет шансов вычислить лексему 1+3. Вернитесь к меловой доске!

Можно было бы уточнить содержание блоков 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, прекращаем работу, отбрасывая этот символ

1 ... 74 75 76 77 78 79 80 81 82 ... 337
Перейти на страницу:
Отзывы - 0

Прочитали книгу? Предлагаем вам поделится своим отзывом от прочитанного(прослушанного)! Ваш отзыв будет полезен читателям, которые еще только собираются познакомиться с произведением.


Уважаемые читатели, слушатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор knigkindom.ru.


Партнер

Новые отзывы

  1. Гость Татьяна Гость Татьяна24 сентябрь 22:20 Как то не очень... Невеста по ошибке. Я не дам тебе развод - Майя Линн
  2. Римма Римма24 сентябрь 21:52 Почему главные героинитпкие идиотки? И сюжет не плохой, и написано хорошо. Но как героиня - так дура дурой.... Хозяйка маленького дома, или Любимая для дракона - Кира Рамис
  3. Римма Римма20 сентябрь 12:27 Много ненужных пояснений и отступлений. Весь сюжет теряет свою привлекательность. Героиня иногда так тупит, что читать не... Хозяйка приюта для перевертышей и полукровок - Елена Кутукова
Все комметарии
Новое в блоге