У меня есть два локальных статических объекта, один и два. Конструктор и деструктор One получают доступ к Two через GetTwo():
#include <iostream>
struct One;
struct Two;
const One& GetOne();
const Two& GetTwo();
struct Two {
const char* value = "It's two!";
Two() { std::cout << "Two construct" << std::endl; }
~Two() { std::cout << "Two destruct" << std::endl; }
};
struct One {
One() {
std::cout << "One construct" << std::endl;
const char* twoval = GetTwo().value;
std::cout << "twoval is: " << twoval << std::endl;
}
~One() {
std::cout << "One destruct" << std::endl;
const char* twoval = GetTwo().value;
std::cout << "twoval is: " << twoval << std::endl;
}
};
const One& GetOne() {
static One one;
return one;
}
const Two& GetTwo() {
static Two two;
return two;
}
int main(void) {
GetOne();
}
Я компилирую это с помощью g++ 4.8.4: g++ -std=c++11 [имя файла]
И выводит:
One construct
Two construct
twoval is: It's two!
One destruct
twoval is: It's two!
Two destruct
Они строятся и разрушаются в одном порядке! Я читал, что для статических переменных классов C++ в одной и той же единице трансляции порядок уничтожения всегда является обратным порядку построения. Но я думаю, что нет? Или это неопределенное поведение?
Кроме того, я слышал, что для C++11 комитет C++ добавил некоторые причудливые гарантии для локальных статических переменных, таких как потокобезопасность. Если не undefined, то является ли такое поведение частью этих гарантий? (Что было бы неплохо, так как это помешало бы вам выстрелить себе в ногу, когда деструктор One использует разрушенный экземпляр Two.) И что гарантируется, если GetOne и GetTwo находятся в разных единицах перевода?
РЕДАКТИРОВАТЬ:
Спасибо за комментарии, теперь я вижу, что объект считается построенным только после возврата его конструктора, а не при первом входе в него, поэтому Two фактически создается до One.
Также я попытался прочитать стандарт и нашел это в стандарте С++ 11, раздел 6.7, пункт 4:
Нулевая инициализация (8.5) всех переменных области блока со статической продолжительностью хранения (3.7.1) или длительностью хранения потока (3.7.2) выполняется до того, как произойдет любая другая инициализация. Константная инициализация (3.6.2) объекта блочной области со статической продолжительностью хранения, если это применимо, выполняется до первого ввода его блока. ...такая переменная инициализируется в первый раз, когда управление проходит через ее объявление; такая переменная считается инициализированной после завершения ее инициализации.
А для разрушения 6.7 указывает нам на 3.6.3, в котором говорится:
Если завершение конструктора или динамическая инициализация объекта со статической продолжительностью хранения следуют до завершения другого, завершение деструктора второго упорядочено до инициации деструктора первого.
Так что, если я правильно понимаю: для локальных статических объектов их построение «секвенируется» во время выполнения в зависимости от порядка вызова функций. И независимо от того, в какой единице трансляции они определены, они будут уничтожены в порядке, обратном порядку, зависящему от времени выполнения.
Это звучит правильно? Это сделало бы это хорошим решением фиаско инициализации статического порядка. Тем не менее, я думаю, что вы все еще можете выстрелить себе в ногу с кодом ниже:
#include <iostream>
struct One;
struct Two;
const One& GetOne();
const Two& GetTwo();
void PrintOneValue(const One& one);
struct Two {
Two() { std::cout << "Two construct" << std::endl; }
~Two() {
std::cout << "start Two destruct" << std::endl;
PrintOneValue(GetOne());
std::cout << "end Two destruct" << std::endl;
}
};
struct One {
const char* value = "It's one!";
One() {
std::cout << "start One construct" << std::endl;
GetTwo();
std::cout << "end One construct" << std::endl;
}
~One() {
std::cout << "One destruct" << std::endl;
}
};
void PrintOneValue(const One& one) {
std::cout << "One's value is: " << one.value << std::endl;
}
const One& GetOne() {
static One one;
return one;
}
const Two& GetTwo() {
static Two two;
return two;
}
int main(void) {
GetOne();
}
Что выводит:
start One construct
Two construct
end One construct
One destruct
start Two destruct
One's value is: It's one!
end Two destruct
Он обращается к данным One после их уничтожения, поэтому поведение undefined. Но, по крайней мере, это детерминировано.
GetTwo
до того, как статический экземплярOne
будет полностью создан. - person Captain Obvlious   schedule 16.07.2015