Цикл жизни non-local static storage duration переменных



В прошлом посте поговорили про локальные статические переменные и их цикл жизни. Сегодня в общем рассмотрим рождение и смерть всех нелокальных глобальных переменных.



Здесь важна оговорка, что объекты со static storage duration не обязаны быть помечены ключевым словом static! Этот термин употребляется для описания объектов, лишь время жизни которых является статическим. То есть более менее всех глобальных объектов. Все они существуют от момента создания до момента завершения программы. Поэтому просто написав:



int var = 1;







считайте, что вы объявили переменную со static storage duration.



Для краткости, вместо "объект со static storage duration" буду писать"глобальный объект".



Так вот. Для таких объектов существует строгий порядок инициализации, который состоит из определенных шагов и подшагов.



1️⃣ Статическая инициализация. В сущности, это установление значения, которое может быть проведено во время компиляции. Состоит из двух подшагов:



👉🏿 Первым, если возможно, идет константная инициализация. Проводится, когда инициализатор - константное выражение.



👉🏿 Во всех остальных случаях проводится Zero-инициализация.



2️⃣ Динамическая инициализация. Только после того, как проведена статическая инициализация, вступает в игру динамическая. Которая и является причиной static initialization order fiasco. Потому что дает очень мало гарантий по поводу порядка инициализации, одна из которых описана тут. Но в общем и целом, порядок инициализации глобальных объектов в разных юнитах трансляции не определен.

Обычно она происходит в рантайме, но если компилятор может, то он производит ее в compile-time при наличии определенных условий.



После инициализации переменная живет в течение всего времени существования программы до тех пор, пока она не завершится.



Если чуть подробнее и конкретнее про завершение, то при выходе из функции main происходят все стандартные процессы разрушения локальных переменных, но еще и вызов std::exit с возращаемым из мэйна значением в качестве аргумента. И вот std::exit одним из своих шагов триггерит вызов деструкторов глобальных объектов.



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



Это суперобобщенно, поэтому дальше будем раскрывать все эти стадии.



Cегодня без картинки, но вместо этого можете посмотреть короткий веселый видосик про цикл жизни программиста



Define cycle of your life. Stay cool.



#cppcore #compiler