Могу ли я изменить статический порядок инициализации переменных в С++?

Я использую С++ в 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".