Каково время жизни статической переменной в функции C++?

Если переменная объявлена ​​как static в области видимости функции, она инициализируется только один раз и сохраняет свое значение между вызовами функций. Каков срок его жизни? Когда вызываются его конструктор и деструктор?

void foo() 
{ 
    static string plonk = "When will I die?";
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
389
0
183 824
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Время жизни переменных функции static начинается, когда [0] поток программы впервые встречает объявление, и заканчивается при завершении программы. Это означает, что среда выполнения должна вести бухгалтерский учет, чтобы разрушить ее, только если она действительно была построена.

Кроме того, поскольку в стандарте говорится, что деструкторы статических объектов должны выполняться в порядке, обратном завершению их построения [1], а порядок построения может зависеть от конкретного запуска программы, порядок построения должен быть принят во внимание.

Пример

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

Выход:

C:>sample.exe
Created in foo
Destroyed in foo

C:>sample.exe 1
Created in if
Created in foo
Destroyed in foo
Destroyed in if

C:>sample.exe 1 2
Created in foo
Created in if
Destroyed in if
Destroyed in foo

[0] Поскольку C++ 98[2] не имеет ссылки на несколько потоков, как это будет вести себя в многопоточной среде, не указано и может быть проблематичным, как упоминает Родди.

[1]C++ 98 раздел 3.6.3.1[basic.start.term]

[2] В C++ 11 статика инициализируется поточно-безопасным способом, это также известно как Магическая статика.

Для простых типов, не имеющих побочных эффектов c'tor / d'tor, можно выполнить прямую оптимизацию, чтобы инициализировать их так же, как глобальные простые типы. Это позволяет избежать проблем с ветвлением, флагом и порядком уничтожения. Это не значит, что их продолжительность жизни какая-то другая.

John McFarlane 29.10.2011 05:06

Если функция может быть вызвана несколькими потоками, значит ли это, что вам нужно убедиться, что статические объявления должны быть защищены мьютексом в C++ 98 ??

allyourcode 09.10.2014 04:57

@allyourcode, я обновил ответ, чтобы сослаться на C++ 11, относительно мьютекса в C++ 98, Ага.

Motti 10.10.2014 00:09

«деструкторы» глобальных объектов должны выполняться в порядке, обратном завершению их построения »здесь не применяется, потому что эти объекты не являются глобальными. Порядок уничтожения локальных переменных со статической или поточной продолжительностью хранения значительно сложнее, чем чистый LIFO, см. Раздел 3.6.3 [basic.start.term]

Ben Voigt 16.06.2015 17:10

@BenVoigt, спасибо за исправление, но из того, что я вижу в C++ 98, это в значительной степени LIFO. C++ 11 немного усложняет работу с thread_local.

Motti 16.06.2015 17:33

Фраза «при завершении программы» не совсем правильна. А как насчет статики в DLL Windows, которые загружаются и выгружаются динамически? Очевидно, что стандарт C++ вообще не касается сборок (было бы неплохо, если бы это было так), но было бы хорошо уточнить, что именно здесь говорится в стандарте. Если бы была включена фраза «при завершении программы», это технически сделало бы любую реализацию C++ с динамически выгружаемыми сборками несоответствующей.

Roger Sanders 14.04.2017 15:25

@RogerSanders, это хороший момент, стандарт, который у меня есть (C++ 11), говорит, что деструкторы объектов со статической продолжительностью хранения вызываются в результате возврата из main и в результате вызова std::exit. Можете ли вы указать мне на часть стандарта, где явно разрешены динамические библиотеки?

Motti 17.04.2017 22:41

@Motti Я не верю, что стандарт явно разрешает использование динамических библиотек, но до сих пор я также не верил, что в стандарте есть что-то конкретное, что противоречит его реализации. Конечно, строго говоря, язык здесь не утверждает, что статические объекты нельзя уничтожить раньше другими способами, просто они должны быть уничтожены при возврате из main или вызове std :: exit. Я считаю, что это довольно тонкая грань.

Roger Sanders 28.04.2017 03:26

Ваш первый абзац верен только для объектов с непустой инициализацией. Для других объектов (например, static int x = 1;) время жизни начинается с момента получения хранилища, то есть при запуске программы для объектов со статической продолжительностью хранения (basic.life/1)

M.M 09.12.2018 14:02

Мотти прав насчет порядка, но есть еще кое-что, что следует учитывать:

Компиляторы обычно используют скрытую переменную флага, чтобы указать, была ли локальная статика уже инициализирована, и этот флаг проверяется при каждой записи функции. Очевидно, это небольшой удар по производительности, но больше беспокоит то, что потокобезопасность этого флага не гарантируется.

Если у вас есть локальная статика, как указано выше, и foo вызывается из нескольких потоков, у вас могут быть условия гонки, вызывающие неправильную или даже многократную инициализацию plonk. Кроме того, в этом случае plonk может быть разрушен потоком, отличным от того, который его создал.

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

C++ 0x требует, чтобы статическая инициализация была потокобезопасной. Так что будьте осторожны, но все будет только лучше.

deft_code 07.10.2010 09:05

Проблем с порядком уничтожения можно избежать с помощью небольшой политики. статические / глобальные объекты (одиночные объекты и т. д.) не должны обращаться к другим статическим объектам в своих телах методов. Доступ к ним должен осуществляться только в конструкторах, где ссылка / указатель может быть сохранена для последующего доступа в методах. Это не идеально, но должно исправить 99 случаев, а случаи, которые он не улавливает, явно сомнительны и должны быть обнаружены при проверке кода. Это по-прежнему не идеальное решение, так как политика не может быть применена на языке

deft_code 07.10.2010 09:15

Я немного новичок, но почему эту политику нельзя применять на языке?

cjcurrie 21.01.2013 09:02

Начиная с C++ 11, это больше не проблема. Ответ Мотти обновлен в соответствии с этим.

Nilanjan Basu 28.12.2017 22:24

FWIW, Codegear C++ Builder не выполняет деструкцию в ожидаемом порядке согласно стандарту.

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

... что является еще одной причиной не полагаться на приказ об уничтожении!

Не лучший аргумент. Я бы сказал, что это скорее аргумент не использовать этот компилятор.

Martin York 29.10.2008 22:24

Хм. Если вы заинтересованы в создании реального переносимого кода, а не просто теоретически переносимого кода, я думаю, полезно знать, какие области языка могут вызывать проблемы. Я был бы удивлен, если бы C++ Builder не справился с этой задачей.

Roddy 29.10.2008 22:58

Я согласен, за исключением того, что я бы сформулировал это как «какие компиляторы вызывают проблемы и в каких областях языка они это делают» ;-P

Steve Jessop 30.10.2008 03:16

Существующие объяснения на самом деле не полны без фактического правила из Стандарта, найденного в 6.7:

The zero-initialization of all block-scope variables with static storage duration or thread storage duration is performed before any other initialization takes place. Constant initialization of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope. Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

Статические переменные вступают в игру после запуск программы и остаются доступными до завершения выполнения программы.

Статические переменные создаются в Сегмент данных в памяти.

это неверно для переменных в области действия функции

awerries 01.06.2020 23:36

Другие вопросы по теме