Область видимости и время жизни
Представьте, что каждый блок программы — комната со своими коробками. Коробку, которую поставили в комнате, видно только в ней: за дверью её уже не найти по наклейке. В C++ стенами комнаты служат фигурные скобки. Имя переменной, объявленной внутри них, действует только до закрывающей скобки. Снаружи его нет:
#include <iostream>
int main() {
int total = 0;
{
int bonus = 10;
total = total + bonus;
}
std::cout << total + bonus << '\n'; // ✗
}error: use of undeclared identifier 'bonus'Часть программы, где имя переменной можно использовать, называют её областью видимости. Она начинается с объявления и заканчивается на закрывающей скобке блока — фрагмента кода в фигурных скобках. Тело функции тоже блок. Поэтому переменные с одним именем в разных функциях — это разные переменные.
Сокрытие имени
Во вложенном блоке можно объявить переменную с тем же именем, что и снаружи. Это новая переменная, как коробка с той же наклейкой, поставленная во внутренней комнате: до конца блока имя обозначает её, а внешняя скрыта. Такое сокрытие заканчивается на закрывающей скобке, и внешняя переменная снова видна с прежним значением:
#include <iostream>
int main() {
int score = 5;
{
int score = 100;
std::cout << score << '\n'; // 100
}
std::cout << score << '\n'; // 5
}Время жизни
Время жизни — срок, в течение которого переменная существует в памяти. Область видимости — свойство имени в тексте программы, а время жизни — свойство самой переменной во время работы. Обычная переменная внутри функции создаётся, когда выполнение доходит до объявления, и уничтожается в конце блока. При следующем вызове функции она создаётся заново. Это как коробка, которую выбрасывают, когда выходят из комнаты, а при следующем визите приносят новую.
Переменная со словом static живёт до конца программы: её коробка остаётся на полке в комнате, и при каждом визите содержимое на месте. Её инициализируют один раз, а не при каждом вызове, и между вызовами она хранит значение:
#include <iostream>
void visit() {
static int visits = 0;
int steps = 0;
visits = visits + 1;
steps = steps + 1;
std::cout << visits << ' ' << steps << '\n';
}
int main() {
visit(); // 1 1
visit(); // 2 1
visit(); // 3 1
}steps при каждом вызове начинается с 0, а visits продолжает счёт. static меняет только время жизни: видна visits по-прежнему лишь внутри visit. Что выведет функция со static-счётчиком, часто спрашивают на собеседовании.
Пройдите эту программу по шагам и следите за рамками: visits лежит в рамке static и переживает каждый вызов, а steps появляется и исчезает вместе с visit.
1–2#include <iostream>3void visit() {4 static int visits = 0;5 int steps = 0;6 visits = visits + 1;7 steps = steps + 1;8 std::cout << visits << " ";9 std::cout << steps << "\n";10}11 12int main() {13 visit();14 visit();15 visit();16}
visits уже существует и равна 0, хотя visit ещё не вызывали
Частая ошибка
Хотели изменить внешнюю переменную внутри блока, но по привычке написали тип ещё раз:
#include <iostream>
int main() {
int total = 0;
{
int bonus = 10;
int total = bonus; // ✗
std::cout << total << '\n'; // 10
}
std::cout << total << '\n'; // 0
}Строка с int объявляет новую переменную, которая скрывает внешнюю total, и внешняя остаётся равной 0. Компилятор не сообщает ни об ошибке, ни о предупреждении. Чтобы изменить внешнюю переменную, нужно присваивание без типа: total = total + bonus;.
Главное
- Область видимости — часть программы, где можно использовать имя: от объявления до закрывающей скобки блока.
- Переменная с тем же именем во вложенном блоке скрывает внешнюю до конца блока, а внешняя сохраняет прежнее значение.
- Обычная переменная в функции создаётся заново при каждом вызове, а
staticинициализируется один раз и хранит значение между вызовами. staticменяет время жизни, но не область видимости.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.