Что заставляет статическую переменную инициализироваться только один раз? - PullRequest
46 голосов
/ 06 апреля 2011

Я заметил, что если вы инициализируете статическую переменную в C ++ в коде, инициализация запускается только при первом запуске функции.

Это круто, но как это реализовать?Это переводит к какому-то искаженному утверждению if?(если дано значение, то ..)

void go( int x )
{
    static int j = x ;
    cout << ++j << endl ; // see 6, 7, 8
} 

int main()
{
    go( 5 ) ;
    go( 5 ) ;
    go( 5 ) ; 
}

Ответы [ 4 ]

51 голосов
/ 06 апреля 2011

Да, обычно он переводится в неявный оператор if с внутренним логическим флагом.Итак, в самой базовой реализации ваше объявление обычно переводится в нечто вроде

void go( int x ) {
  static int j;
  static bool j_initialized;

  if (!j_initialized) {
    j = x;
    j_initialized = true;
  }

  ...
} 

Кроме того, если у вашего статического объекта есть нетривиальный деструктор, язык должен подчиняться другому правилу: такие статические объектыдолжны быть уничтожены в обратном порядке их строительства.Поскольку порядок построения известен только во время выполнения, порядок уничтожения также определяется во время выполнения.Таким образом, каждый раз, когда вы создаете локальный статический объект с нетривиальным деструктором, программа должна регистрировать его в каком-то линейном контейнере, который впоследствии будет использоваться для уничтожения этих объектов в правильном порядке.

Нет необходимостискажем, фактические детали зависят от реализации.


Стоит добавить, что когда речь идет о статических объектах «примитивных» типов (например, int в вашем примере), инициализированных с помощью констант времени компиляции,компилятор может инициализировать этот объект при запуске.Вы никогда не заметите разницу.Однако, если вы возьмете более сложный пример с «не примитивным» объектом

void go( int x ) {
  static std::string s = "Hello World!";
  ...

, то вышеупомянутый подход с if - это то, что вы должны ожидать найти в сгенерированном коде, даже когда объектинициализируется константой времени компиляции.

В вашем случае инициализатор неизвестен во время компиляции, что означает, что компилятор должен отложить инициализацию и использовать неявный if.

7 голосов
/ 06 апреля 2011

Да, компилятор обычно генерирует скрытое логическое значение "это было инициализировано?" флаг и if, который запускается каждый раз при выполнении функции.

Здесь есть больше материалов для чтения: Как инициализация статической переменной осуществляется компилятором?

2 голосов
/ 06 апреля 2011

Хотя это действительно «какое-то искривление, если», поворот может быть больше, чем вы себе представляли ...

Комментарий ZoogieZork к ответу AndreyT затрагивает важный аспект: инициализация статических локальных переменных - на некоторые компиляторы , включая GCC - , по умолчанию безопасны для потоков (опция командной строки компилятора может отключить его). Следовательно, он использует некоторый механизм синхронизации между потоками (мьютекс или атомарную операцию некоторого вида), который может быть относительно медленным . Если вам неудобно - с точки зрения производительности - с явным использованием такой операции в вашей функции, то вам следует подумать о том, есть ли альтернатива ленивой инициализации переменной с меньшим воздействием (то есть явно создайте ее безопасным для себя способом где-то только один раз). Очень немногие функции настолько чувствительны к производительности, что это важно - не позволяйте им испортить ваш день или усложните ваш код, если ваши программы не работают слишком медленно и ваш профилировщик не перебирает эту область.

1 голос
/ 06 апреля 2011

Они инициализируются только один раз, потому что это то, что предписывает стандарт C ++. Как это происходит, зависит только от поставщиков компиляторов. По моему опыту, локальный скрытый флаг генерируется и используется компилятором.

...