Відповіді:
Термін служби 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
Створено у foo
Знищено у fooC:> sample.exe 1
Створено, якщо
створено в foo
Знищено в foo
Знищено в ifC:> sample.exe 1 2
Створено у foo
Створено, якщо
знищено, якщо
знищено у foo
[0]
Оскільки C ++ 98 [2] не має посилання на кілька потоків, то, як це буде вести себе у багатопотоковому середовищі, не визначено, і це може бути проблематично, як згадує Родді .
[1]
Розділ C ++ 98 3.6.3.1
[basic.start.term]
[2]
У C ++ 11 статика ініціалізується безпечним потоком, це також відоме як Magic Statics .
[basic.start.term]
Мотті має рацію щодо порядку, але слід врахувати ще деякі речі:
Компілятори зазвичай використовують приховану змінну прапора, щоб вказати, чи локальна статика вже ініціалізована, і цей прапор перевіряється при кожному записі функції. Очевидно, що це невеликий хіт продуктивності, але що більше хвилює, що цей прапор не гарантовано є безпечним для потоків.
Якщо ви маєте локальну статику, як описано вище, і foo
викликається з декількох потоків, у вас можуть бути умови перегонів, plonk
які можуть ініціалізуватися неправильно або навіть кілька разів. Також у цьому випадку plonk
може бути зруйнований інший потік, ніж той, який його сконструював.
Незважаючи на те, що говорить стандарт, я б дуже насторожено ставився до фактичного порядку локального статичного знищення, оскільки можливо, ви можете мимоволі розраховувати на те, що статика все ще діє після її руйнування, і це справді важко відстежити.
Існуючі пояснення насправді не завершені без фактичного правила зі Стандарту, наведеного в 6.7:
Нульова ініціалізація всіх змінних блоку області зі статичною тривалістю зберігання або тривалістю зберігання потоку виконується до того, як відбудеться будь-яка інша ініціалізація. Постійна ініціалізація об'єкта блоку блоку зі статичною тривалістю зберігання, якщо це застосовується, виконується до першого введення його блоку. Реалізація дозволена здійснювати ранню ініціалізацію інших змінних блоку області із статичною тривалістю або тривалістю зберігання при тих самих умовах, що реалізація дозволена статично ініціалізувати змінну зі статичною тривалістю або тривалістю зберігання потоку в області простору імен. В іншому випадку така змінна ініціалізується, коли перший час управління проходить через її оголошення; така змінна вважається ініціалізованою після завершення її ініціалізації. Якщо ініціалізація закінчується, викидаючи виняток, ініціалізація не завершена, тому буде повторено спробу наступного разу, коли контроль введе декларацію. Якщо управління вводить декларацію одночасно під час ініціалізації змінної, одночасне виконання повинно чекати завершення ініціалізації. Якщо управління повторно вводить декларацію рекурсивно під час ініціалізації змінної, поведінка не визначена.
FWIW, Codegear C ++ Builder не руйнує в очікуваному порядку відповідно до стандарту.
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
... це ще одна причина не покладатися на наказ про знищення!
У змінних статичних прийшов в гру , як тільки починає виконання програми і залишається доступними до кінців виконання програми.
Статичні змінні створюються в сегменті даних пам'яті .