У меня есть код C ++, который объявляет статические переменные времени жизни, которые инициализируются вызовами функций. Вызываемая функция создает vector
экземпляр и называет его push_back
метод. Является ли код рискованным из-за фиаско статического порядка инициализации C ++? Если нет, то почему нет?
Дополнительная информация:
Что такое «фиаско статического порядка инициализации»?
Это объясняется в C ++ FAQ 10.14
Почему я думаю, что использование вектора может вызвать фиаско?
Возможно, что vector
Конструктор использует значение другой статической переменной времени жизни, инициализированной динамически. Если это так, то нет ничего, чтобы гарантировать, что vector
переменная инициализируется перед использованием vector
в моем коде. Инициализация result
(см. код ниже) может в конечном итоге вызвать vector
конструктор до vector
Зависимости полностью инициализируются, что приводит к доступу к неинициализированной памяти.
Как этот код выглядит в любом случае?
struct QueryEngine {
QueryEngine(const char *query, string *result_ptr)
: query(query), result_ptr(result_ptr) { }
static void AddQuery(const char *query, string *result_ptr) {
if (pending == NULL)
pending = new vector<QueryEngine>;
pending->push_back(QueryEngine(query, result_ptr));
}
const char *query;
string *result_ptr;
static vector<QueryEngine> *pending;
};
vector<QueryEngine> *QueryEngine::pending = NULL;
void Register(const char *query, string *result_ptr) {
QueryEngine::AddQuery(query, result_ptr);
}
string result = Register("query", &result);
К счастью, static
объекты инициализируются нулями еще до любой выполняется другая инициализация (даже до «истинной» инициализации тех же объектов), поэтому вы знаете, что NULL
будет установлен на этот указатель задолго до Register
сначала вызывается.1
Теперь, с точки зрения работы с вашим вектором, кажется, что (технически) вы мог столкнуться с такой проблемой:
[C++11: 17.6.5.9/3]:
Функция стандартной библиотеки C ++ не должна прямо или косвенно изменять объекты (1.10), доступные для потоков, отличных от текущего потока, если к объектам не обращаются прямо или косвенно через неконстантные аргументы функции, включаяthis
,
[C++11: 17.6.5.9/4]:
[Заметка: Это означает, например, что реализации не могут использовать статический объект для внутренних целей без синхронизации, потому что это может вызвать гонку данных даже в программах, которые не разделяют объекты явно между потоками. —Конечная записка]
Обратите внимание, что хотя в этой заметке требуется синхронизация, она упоминается в отрывке, в котором в конечном итоге признается, что static
детали реализации в противном случае допускаются.
При этом представляется, что в стандарте следует дополнительно указать, что пользовательский код должен избегать работы со стандартными контейнерами во время статической инициализации, если предполагается, что семантика такого кода не может быть гарантирована; Я бы посчитал это недостатком стандарта, так или иначе. Это должно быть понятнее.
1 И это является указатель NULL, каким бы ни было его побитовое представление, а не блот до нуля.
vector
не зависит от того, что мешает его использованию при динамической инициализации статики. Единственная проблема в вашем коде — это отсутствие безопасности потоков — нет особой причины думать, что вы должны заботиться об этом, если только у вас нет статики, конструкция которой порождает потоки ….
Инициализация
result
(см. код ниже) может закончить вызовом векторного конструктора до полной инициализации этого класса, что приведет к доступу к неинициализированной памяти.
Нет … инициализация result
звонки AddQuery
который проверяет if (pending == NULL)
— инициализация до NULL
безусловно, будет выполнено до любой динамической инициализации, согласно 3.6.2 / 2:
Постоянная инициализация выполняется:
…
— если объект со статическим или нить срок хранения не инициализируется вызовом конструктора и если объект инициализируется значением или каждое полное выражение, которое появляется в его инициализаторе, является константным выражением
Так что даже если result
назначение находится в другой единице перевода, это безопасно. См. 3.6.2 / 2:
Вместе нулевая инициализация и постоянная инициализация называются статическая инициализация; все остальные инициализации динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.