Могу ли я изменить статический порядок инициализации переменных в С++?
Я использую С++ в Visual Studio 2015 sp3.
По
#pragma init_seg(compiler)
сначала инициализирую некоторые статические переменные (для управления памятью).
https://msdn.microsoft.com/en-us/library/7977wcck.aspx
Но существует
#pragma init_seg(compiler)
в wcerr.cpp(Microsoft Visual Studio 14.0\VC\crt\src\stl\wcerr.cpp), поэтому эти объекты инициализируются перед моими объектами.
Можно ли принудительно инициализировать мой объект перед объектами wcerr.cpp
любыми параметрами компиляции/ссылок?
Ответы
Ответ 1
Одним из решений является попытка переноса ваших статических переменных на статические функции:
static type& My_static_obj() {
static type my_static_obj_;
return my_static_obj_;
}
Он выглядит как простой тип Singleton и вызывает Construct On First Use Idiom. Из-за стандартного (С++ 11 и выше) он гарантированно инициализируется один раз (и даже атомарно!), И внутри его c-tor такой объект может иметь доступ к другим "статическим" переменным, поэтому, если нет кругового зависимостей между переменными, порядок инициализации будет строго определен.
Дополнительную информацию см. в этом вопросе и другие описания этой конструкции на первом идиоме использования.
Ответ 2
Вероятно, отличный счетчик может помочь вам как-то в этом случае:
Убедитесь, что нелокальный статический объект инициализирован до его первого использования и уничтожен только после последнего использования объекта.
Его мотивация довольно ясна:
Когда статические объекты используют другие статические объекты, проблема инициализации становится более сложной. Статический объект должен быть инициализирован перед его использованием, если он имеет нетривиальную инициализацию. Порядок инициализации статических объектов в единицах компиляции не определен. Несколько статических объектов, распространяемых по нескольким единицам компиляции, могут использовать один статический объект. Поэтому он должен быть инициализирован перед использованием. Одним из примеров является std:: cout, который обычно используется рядом других статических объектов.
Стоит скопировать и вставить непосредственно пример с указанной выше связанной страницы:
Stream.h
#ifndef STREAM_H
#define STREAM_H
struct Stream {
Stream ();
~Stream ();
};
extern Stream& stream; // global stream object
static struct StreamInitializer {
StreamInitializer ();
~StreamInitializer ();
} streamInitializer; // static initializer for every translation unit
#endif // STREAM_H
Stream.cpp
#include "Stream.h"
#include <new> // placement new
#include <type_traits> // aligned_storage
static int nifty_counter; // zero initialized at load time
static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type
stream_buf; // memory for the stream object
Stream& stream = reinterpret_cast<Stream&> (stream_buf);
Stream::Stream ()
{
// initialize things
}
Stream::~Stream ()
{
// clean-up
}
StreamInitializer::StreamInitializer ()
{
if (nifty_counter++ == 0) new (&stream) Stream (); // placement new
}
StreamInitializer::~StreamInitializer ()
{
if (--nifty_counter == 0) (&stream)->~Stream ();
}
Заголовочный файл класса Stream должен быть включен до того, как любая функция-член может быть вызвана в объекте Stream. Экземпляр класса StreamInitializer включен в каждый блок компиляции. Любое использование объекта Stream следует за включением заголовка, который гарантирует, что конструктор объекта инициализатора вызывается до использования объекта Stream.
Подробнее см. ссылку выше.
Ответ 3
EDIT: Я думаю, что решение, которое вам нужно, это использовать другую реализацию STL, если вам нужно использовать MSVC 2015, а также избегать переопределения вашего текущего взлома управления памятью (извините, что назовите это, но то, что это звучит, но затем снова Я использовал несколько собственных ручек для работы с MSVC).
Я никогда не использовал реализацию STL, которая не была включена в среду разработки, но это возможно. Это должно быть простой вопрос - установить пути включения и библиотеки в альтернативную реализацию STL. Вы можете начать с попытки STLPort
В противном случае, мой первоначальный совет относительно вашего вопроса, до последнего редактирования:
Статический порядок инициализации в блоке компиляции (файл cpp) - это порядок, в котором сделаны объявления. Статический порядок инициализации между единицами компиляции undefined. Не используйте статическую инициализацию для решения этой проблемы.
Похоже, что RustyX указывает вам в правильном направлении с его комментарием, ссылающимся на Переопределение памяти в MSVС++.
EDIT: похоже, ответ, который вы ищете, может быть здесь: Как правильно заменить глобальные новые и удалить операторы. Обратите внимание, что оба ответа актуальны, вы захотите заменить new
, delete
, new[]
и delete[]
. Поскольку это применяется во время связывания, очевидно, Windows не будет использовать их для загружаемых DLL (см. Комментарии к первому ответу).
Ответ 4
вы можете получить представление от проекта Chromium Docs. См. Раздел "Singleton and base:: LazyInstance".