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

Когда я использую статические переменные в С++, я часто теряю желание инициализировать одну переменную, передавая ее другому конструктору. Другими словами, я хочу создать статические экземпляры, которые зависят друг от друга.

Внутри одного файла .cpp или .h это не проблема: экземпляры будут созданы в том порядке, в котором они объявлены. Однако, если вы хотите инициализировать статический экземпляр экземпляром в другом модуле компиляции, порядок представляется невозможным. В результате, в зависимости от погоды, может случиться так, что экземпляр, который зависит от другого, строится, и только после этого создается другой экземпляр. В результате первый экземпляр инициализируется неправильно.

Кто-нибудь знает, как обеспечить, чтобы статические объекты были созданы в правильном порядке? Я долго искал решение, пытаясь всех (включая решение Schwarz Counter), но я начинаю сомневаться, что есть тот, который действительно работает.

Одна из возможностей - это трюк со статическим членом функции:

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

Действительно, это действительно работает. К сожалению, вам нужно написать globalObject(). MemberFunction() вместо globalObject.MemberFunction(), что приводит к несколько запутанному и неэлегантному клиентскому коду.

Обновление: Благодарим за ваши реакции. К сожалению, мне кажется, что я ответил на свой вопрос. Думаю, мне придется научиться жить с ним...

Ответ 1

Вы ответили на свой вопрос. Статический порядок инициализации undefined, и самый элегантный способ его (хотя и выполняет статическую инициализацию, т.е. Не рефакторинг) полностью завершает инициализацию в функции.

Прочитайте часто задаваемые вопросы по С++, начиная с https://isocpp.org/wiki/faq/ctors#static-init-order

Ответ 2

Возможно, вам стоит пересмотреть, нужно ли вам так много глобальных статических переменных. Хотя иногда они могут быть полезны, зачастую гораздо проще реорганизовать их на меньшую локальную область, особенно если вы обнаружите, что некоторые статические переменные зависят от других.

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

Ответ 3

Действительно, это действительно работает. К сожалению, вам нужно написать globalObject(). MemberFunction() вместо globalObject.MemberFunction(), что приводит к несколько запутанному и неэлегантному клиентскому коду.

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

Правильность программы должна быть вашей первоочередной задачей. Кроме того, ИМХО,() выше, является чисто стилистическим - т.е. совершенно неважно.

В зависимости от вашей платформы будьте осторожны с слишком большой динамической инициализацией. Существует относительно небольшое количество очистки, которое может иметь место для динамических инициализаторов (см. здесь). Вы можете решить эту проблему, используя глобальный контейнер объекта, который содержит элементы разных глобальных объектов. Поэтому у вас есть:

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

Существует только один вызов ~ Globals(), чтобы очистить все глобальные объекты в вашей программе. Чтобы получить доступ к глобальному, у вас все еще есть что-то вроде:

getGlobals().configuration.memberFunction ();

Если вы действительно хотели, вы могли бы обернуть это в макрос, чтобы сохранить крошечный бит ввода с помощью макроса:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

Хотя, это просто синтаксический сахар в вашем первоначальном решении.

Ответ 4

Большинство компиляторов (линкеров) фактически поддерживают (не переносимый) способ указания порядка. Например, с помощью visual studio вы можете использовать init_seg, чтобы организовать инициализацию в несколько разных групп. AFAIK не может гарантировать порядок в каждой группе. Поскольку это не переносится, вы можете подумать, можете ли вы исправить свой дизайн, чтобы он не требовал его, но опция отсутствует.

Ответ 5

укажите возраст этой темы, я хотел бы предложить решение, которое я нашел. Как указывали многие из меня, С++ не предоставляет никакого механизма для статического инициализации. Я предлагаю инкапсулировать каждый статический член внутри статического метода класса, который, в свою очередь, инициализирует член и обеспечивает доступ объектно-ориентированным способом. Позвольте мне привести вам пример, предположим, что мы хотим определить класс с именем "Math", который среди других членов содержит "PI":

class Math {
public:
   static const float Pi() {
       static const float s_PI = 3.14f;
       return s_PI;
   }
}

s_PI будет инициализирован при первом вызове метода Pi() (в GCC). Имейте в виду: локальные объекты со статическим хранилищем имеют зависимый от реализации жизненный цикл, для более подробной проверки 6.7.4 в 2.

Статическое ключевое слово, Стандарт С++

Ответ 6

Обертка статичности в методе будет исправлять проблему порядка, но она не является потокобезопасной, как указывали другие, но вы можете сделать это, чтобы сделать ее ниткой, если это вызывает беспокойство.

// File scope static pointer is thread safe and is initialized first.
static Type * theOneAndOnlyInstance = 0;

Type& globalObject()
{
    if(theOneAndOnlyInstance == 0)
    {
         // Put mutex lock here for thread safety
         theOneAndOnlyInstance = new Type();
    }

    return *theOneAndOnlyInstance;
}