Вопрос

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

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

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

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

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

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

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

Это было полезно?

Решение

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

Прочитайте раздел часто задаваемых вопросов по C ++, начиная с https://isocpp.org/wiki / Вопросы / ctors # статических INIT порядка

Другие советы

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

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

  

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

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

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

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

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

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

getGlobals().configuration.memberFunction ();

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

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

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

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

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

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

// 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top