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


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

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

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

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

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

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

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

6   52   2009-06-17 12:00:35

6 ответов:

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

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

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

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

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

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

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

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

static ключевое слово,Стандарт 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;
}