1. Корректность (вы могли изменить код, оставив старый комментарий).
2. Адекватность (редкое качество).
3. Немногословность (чтобы не отпугнуть читателя).
Подчеркнем важность последнего свойства: все, что необходимо сказать в коде, следует выражать средствами самого языка программирования. Избегайте комментариев, описывающих то, что и так совершенно понятно для тех, кто знает язык программирования. Рассмотрим пример.
x = b+c; //
складываем переменные b и c и присваиваем результат
// переменной x
Такие комментарии часто можно встретить в учебниках, но они нужны лишь для того, чтобы объяснить свойства языка, которые еще не известны читателям. Комментарии нужны для того, чтобы объяснять то, что сложно выразить средствами языка программирования. Примером такой ситуации является выражение намерения программиста: код означает лишь то, что программа делает на самом деле, но он ничего не может сказать читателю о действительных намерениях программиста (см. раздел 5.9.1). Посмотрите на код программы калькулятора. В нем кое-чего не хватает: функции описывают, как мы обрабатываем выражения и лексемы, но ничего не сказано (помимо самого кода) о том, что именно мы считаем выражением и лексемой. Лучше всего поместить такие комментарии в грамматике.
/*
Простой калькулятор
История версий:
Переработан Бьярне Страуструпом в мае 2007 г.
Переработан Бьярне Страуструпом в августе 2006 г.
Переработан Бьярне Страуструпом в августе 2004 г.
Разработан Бьярне Страуструпом
(bs@cs.tamu.edu) весной 2004 г.
Эта программа реализует основные выражения калькулятора.
Ввод из потока с in; вывод в поток cout.
Грамматика для ввода:
Инструкция:
Выражение
Печать
Выход
Печать:
;
Выход:
q
Выражение:
Терм
Выражение + Терм
Выражение – Терм
Терм:
Первичное выражение
Терм * Первичное выражение
Терм / Первичное выражение
Терм % Первичное выражение
Первичное выражение:
Число
(Выражение)
– Первичное выражение
+ Первичное выражение
Число:
литерал_с_плавающей_точкой
Ввод из потока 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
. Следовательно, мы должны убедиться, что в потоке нет лексем, связанных с прекращенными вычислениями и способных помешать будущим вычислениям.