Статический порядок инициализации в классе heirarchy

Я недавно стал мучительно осознавать Статическая инициализация заказа Fiasco. Мне интересно, хотя правило, что «порядок инициализации не определен в единицах перевода» все еще сохраняется для статических членов родительского класса, которые необходимы статическим членам дочернего класса.

Например, скажем, у нас есть (исключая, для краткости, все # охранники и включает в себя)

// a.h
class A {
static int count;
static int register_subclass();
};

// a.cpp
int A::count = 0;
int A::register_subclass() {
return count ++;
}

А потом подклассы A,

// b.h
class B : public A {
static int id;
};

// b.cpp
int B::id = A::register_subclass();

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

Мой вопрос: это действительно безопасно?

То есть я гарантированно, что нет никаких шансов, что B::id будет содержать мусор, скопированный с A::count до того, как последний инициализируется? Из моих собственных тестов, A всегда кажется, что сначала инициализируется, но я не уверен, как ввести шум в порядке инициализации, чтобы увеличить вероятность сбоя, если поведение не определено.

4

Решение

Обычно небезопасно полагаться на статический порядок инициализации базового класса и производного класса. Нет гарантии, что статическая инициализация A произойдет раньше B, Это определение статический порядок инициализации фиаско.

Вы могли бы использовать построить при первом использовании идиома:

// a.h
class A {
private:
static int& count();
protected:
static int register_subclass();
};

// a.cpp
int& A::count() {
static int count = 0;
return count;
}

int A::register_subclass() {
return count()++;
}

// b.h
class B : public A {
public:
static int id;
};

// b.cpp
int B::id = A::register_subclass();

Живая демоверсия.

Обновить: Однако, сказав это, Богдан указал в комментариях

согласно [3.6.2] в Стандарте порядок инициализации в этом конкретном примере гарантирован. Это не имеет ничего общего с наследованием, но с тем фактом, что инициализация A::count является постоянная инициализация, что гарантированно будет сделано раньше динамическая инициализация, который является то, что B::id использует.

Но если у вас нет полного понимания таких внутрикачеств, я рекомендую вам использовать построить при первом использовании идиома.

И это нормально в этом случае, но будьте осторожны с такими функциями, как A::register_subclass в многопоточном контексте. Если несколько потоков вызывают его одновременно, все может произойти.

3

Другие решения

Мне интересно, хотя правило, что «порядок инициализации не определен в единицах перевода» все еще сохраняется для статических членов родительского класса, которые необходимы статическим членам дочернего класса.

Да, это так.

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

0

По вопросам рекламы [email protected]