Глобальные переменные, такие как program_name
и v
, инициализируются до выполнения первой инструкции функции main()
. Они существуют, пока программа не закончит работу, а потом уничтожаются. Они создаются в порядке следования своих определений (т.е. переменная program_name создается до переменной v
), а уничтожаются — в обратном порядке (т.е. переменная v
уничтожается до переменной program_name
).
Когда какая-нибудь функция вызывает функцию f()
, сначала создается переменная s;
иначе говоря, переменная s
инициализируется пустой строкой. Она будет существовать, пока функция f()
не вернет управление. Каждый раз, когда мы входим в тело цикла while
, создаются переменные stripped
и not_letters
. Поскольку переменная stripped
определена до переменной not_letters
, сначала создается переменная stripped
. Они существуют до выхода из тела цикла. В этот момент они уничтожаются в обратном порядке (иначе говоря, переменная not_letters
уничтожается до переменной stripped
) и до того, как произойдет проверка условия выхода из цикла. Итак, если, до того, как мы обнаружим строку quit
, мы выполним цикл десять раз, переменные stripped
и not_letters
будут созданы и уничтожены десять раз.
Каждый раз, когда мы входим в цикл for
, создается переменная i
. Каждый раз, когда мы выходим из цикла for
, переменная i
уничтожается до того, как мы достигнем инструкции v.push_back(stripped);
.
Обратите внимание на то, что компиляторы (и редакторы связей) — довольно разумны и способны оптимизировать код. В частности, компиляторы не выделяют и не освобождают память чаще, чем это действительно требуется.
8.6.1. Вычисление выражения
v[i] = ++i; // неопределенный порядок вычислений
v[++i] = i; // неопределенный порядок вычислений
int x = ++i + ++i; // неопределенный порядок вычислений
cout << ++i << ' ' << i << '\n'; // неопределенный порядок вычислений
f(++i,++i); // неопределенный порядок вычислений
К сожалению, не все компиляторы выдают предупреждение о таких ошибках; это плохо, потому что нельзя рассчитывать на то, что результаты будут одинаковыми при выполнении вычислений на другом компьютере, при использовании других компиляторов или при других установках оптимизатора.
Компиляторы действительно по-разному обрабатывают этот код; избегайте таких ситуаций.
Обратите внимание на то, что оператор =
(присваивание) в выражениях используется наряду с остальными, поэтому нет никакой гарантии того, что левая часть оператора будет вычислена раньше правой части. По этой причине выражение v[++i] = i
имеет неопределенный результат.
8.6.2. Глобальная инициализация
Глобальные переменные (и переменные из пространства имен; раздел 8.7) в отдельной единице трансляции инициализируются в том порядке, в котором они появляются. Рассмотрим пример.
// файл f1.cpp
int x1 = 1;
int y1 = x1+2; // переменная y1 становится равной 3
Эта инициализация логически происходит до выполнения кода в функции main()
. Использование глобальной переменной, за исключением редких ситуаций, нецелесообразно. Мы уже говорили, что не существует эффективного способа, позволяющего программисту определить, какие части программы считывают или записывают переменную (см. раздел 8.4). Другая проблема заключается в том, что порядок инициализации глобальных переменных не определен. Рассмотрим пример.
// файл f2.cpp
extern int y1;
int y2 = y1+2; // переменная y2 становится равной 2 или 5
Такой код нежелателен по нескольким причинам: в нем используются глобальные переменные, которые имеют слишком короткие имена, и сложная инициализация глобальных переменных. Если глобальные переменные в файле f1.cpp
инициализируются до глобальных переменных в файле f2.cpp
, то переменная y2
будет инициализирована числом 5
(как наивно ожидает программист).