Последнее присваивание заслуживает внимания. Во-первых, оно ясно показывает, что знак “равно” не означает равенства, поскольку очевидно, что а не равно а+7. Этот знак означает присваивание, т.е. помещение в переменную нового значения. Рассмотрим подробнее, что происходит при выполнении инструкции a= a+7.
1. Сначала получаем значение переменной a; оно равно целому числу 4.
2. Затем добавляем к четверке семерку, получаем целое число 11.
3. В заключение записываем значение 11 в переменную a.
Эту операцию можно продемонстрировать также на примере строк.
string a = "alpha"; // начальное значение переменной a равно "alpha"
a = "beta"; // переменная a принимает значение "beta"
// (становится равной "beta")
string b = a; // начальное значение переменной b является
// копией значения переменной a (т.е. "beta")
b = a+"gamma"; // переменная b принимает значение a+"gamma"
// (т.е. "betagamma")
a = a+"delta"; // переменная a принимает значение a+"delta"
// (т.е. "betadelta")
В предыдущих примерах мы использовали выражения “начальное значение” и “принимает значение”, для того чтобы отличить похожие, но логически разные операции.
• Инициализация (присваивание переменной ее начального значения).
• Присваивание (запись в переменную нового значения).
Эти операции настолько похожи, что в языке С++ для них используется одно и то же обозначение.
int y = 8; // инициализация переменной y значением 8
x = 9; // присваивание числа 9 переменной x
string t = "howdy!"; // инициализация переменной t значением "howdy!"
s = "G'day"; // присваивание переменной s значения "G’day"
Однако с логической точки зрения присваивание и инициализация различаются. Например, инициализация всегда происходит одновременно с определением типа (например, int или string), а присваивание нет. В принципе инициализация всегда осуществляется с пустой переменной. С другой стороны, присваивание (в принципе) сначала должно стереть старое значение из переменной и лишь затем записать в нее новое значение. Переменную можно представить в виде небольшого ящика, а значение — в виде конкретной вещи (например, монеты), лежащей в этом ящике. Перед инициализацией ящик пуст, но после нее он всегда содержит монету, поэтому, для того чтобы положить в него новую монету, вы (т.е. оператор присваивания) сначала должны вынуть из него старую (“стереть старое значение”), причем ящик нельзя оставлять пустым. Разумеется, в памяти компьютера эти операции происходят не так буквально, как мы описали, но ничего вредного в такой аллегории нет.
3.5.1. Пример: выявление повторяющихся слов
Присваивание необходимо, когда нам требуется записать в объект новое значение. Если подумать, то станет совершенно ясно, что присваивание является особенно полезным, когда приходится повторять операции несколько раз. Присваивание необходимо, когда требуется повторить операцию с новым значением. Рассмотрим небольшую программу, выявляющую повторяющиеся слова в предложении. Такие программы являются частью большинства инструментов для проверки грамматики.
int main()
{
string previous = " "; // переменная previous;
// инициализована "не словом"
string current; // текущее слово
while (cin>>current) { // считываем поток слов
if (previous == current) // проверяем, совпадает ли
// слово с предыдущим
cout << " повторяющееся слово: " << current << 'n';
previous = current;
}
}
Эту программу нельзя назвать очень полезной, поскольку она не способна указать, в каком именно месте стоит повторяющееся слово, но этого для нас пока достаточно. Рассмотрим эту программу строка за строкой.
string current; // текущее слово
Это строковая переменная, в которую мы сразу же считываем текущее (т.е. только что прочитанное) слово с помощью оператора
while (cin>>current)
Эта конструкция, называемая инструкцией while, интересна сама по себе, поэтому мы еще вернемся к ней в разделе 4.4.2.1. Ключевое слово while означает, что инструкция, стоящая следом за выражением cin>>current, будет повторяться до тех пор, пока выполняется операция cin>>current, а операция cin>>current будет выполняться до тех пор, пока в стандартном потоке ввода есть символы.
Напомним, что для типа string оператор считывает слова, отделенные друг от друга разделителями. Этот цикл завершается вводом символа конца ввода (как правило, называемым концом файла). В системе Windows этот символ вводится путем нажатия комбинации клавиш <Ctrl+Z>, а затем — клавиши <Enter>. В системе Unix или Linux для этого используется комбинация клавиш <Ctrl+D>.
Итак, мы должны считать текущее слово из потока ввода и сравнить его с предыдущим словом (уже хранящимся в памяти). Если они окажутся одинаковыми, мы сообщим об этом.
if (previous == current) // проверяем, совпадает ли слово
// с предыдущим
cout << " повторяющееся слово: " << current << 'n';
Теперь мы должны повторить описанную операцию. Для этого копируем значение переменной current в переменную previous.
previous = current;
Эта инструкция учитывает все возможные ситуации, кроме начальной. Что делать с первым словом, у которого нет предыдущего, с которым его следовало бы сравнивать? Эта проблема решается с помощью следующего определения переменной previous:
string previous = " "; // переменная previous; инициализована
// "не словом"
Строка состоит из одного символа (пробела, который вводится путем нажатия клавиши пробела). Оператор ввода >> пропускает разделители, поэтому мы не смогли бы считать этот символ из потока ввода. Следовательно, в ходе первой проверки while сравнение
if (previous == current)
покажет, что значения переменных не совпадают (что и требовалось).
Для того чтобы понять программу, надо на время стать “компьютером”, т.е. умозрительно выполнять программу строка за строкой. Просто нарисуйте квадратики на бумаге, записывайте в них значения и изменяйте их так, как указано в программе.
ПОПРОБУЙТЕ
Выполните эту программу самостоятельно, записывая промежуточные результаты на лист бумаги. Для проверки используйте фразу “The