C ++ статический порядок инициализации - PullRequest
56 голосов
/ 17 июня 2009

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

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

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

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

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

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

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

Ответы [ 6 ]

57 голосов
/ 17 июня 2009

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

Прочитайте пункты C ++ FAQ, начиная с https://isocpp.org/wiki/faq/ctors#static-init-order

8 голосов
/ 17 июня 2009

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

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

6 голосов
/ 17 июня 2009

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

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

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

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

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

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

getGlobals().configuration.memberFunction ();

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

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

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

3 голосов
/ 12 августа 2013

независимо от возраста этой темы, я хотел бы предложить решение, которое я нашел. Как многие указывали ранее, C ++ не предоставляет никакого механизма для статического упорядочения инициализации. Я предлагаю заключить в капсулу каждый статический член внутри статического метода класса, который, в свою очередь, инициализирует член и обеспечивает доступ объектно-ориентированным способом. Позвольте мне привести пример, предположив, что мы хотим определить класс с именем "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 .

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

3 голосов
/ 26 июня 2009

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

1 голос
/ 29 января 2014

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

// 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...