C++ статический порядок инициализации
когда я использую статические переменные в C++, я часто хочу инициализировать одну переменную, передавая другую ее конструктору. Другими словами, Я хочу создать статические экземпляры, которые зависят друг от друга.
в рамках одного .cpp или .H файл это не проблема: экземпляры будут созданы в том порядке, в котором они объявлены. Однако, когда вы хотите инициализировать статический экземпляр с экземпляром в другой единице компиляции, порядок, кажется, невозможно указать. Результат это то, что в зависимости от погоды может случиться так, что экземпляр, который зависит от другого, строится, и только после этого строится другой экземпляр. В результате первый экземпляр инициализируется неправильно.
кто-нибудь знает, как убедиться, что статические объекты создаются в правильном порядке? Я долго искал решение, пытаясь все из них (включая решение Schwarz Counter), но я начинаю сомневаться, что есть один, который действительно завод.
одна из возможностей-это трюк со статической функции-члена:
Type& globalObject()
{
static Type theOneAndOnlyInstance;
return theOneAndOnlyInstance;
}
действительно, это работает. К сожалению, вы должны написать globalObject ().MemberFunction(), вместо globalObject.MemberFunction(), в результате чего несколько запутанный и неизящный код клиента.
обновление: Спасибо за вашу реакцию. К сожалению, мне действительно кажется, что я ответил на свой собственный вопрос. Я думаю, мне придется научиться жить с этим...
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.
обертывание статики в методе исправит проблему порядка, но это не потокобезопасно, как указывали другие, но вы можете сделать это, чтобы также сделать его потоком, если это вызывает беспокойство.
// 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; }