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

55

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

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

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

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

<code>Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}
</code>

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

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

the instances will be created in the order they are defined bartolo-otrit

Ваш Ответ

6   ответов
1

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

// 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;
}
Верхний ответ предполагает именно это, и на него ответили почти 5 лет назад. Возможно, нам следует перенести это в верхний ответ в качестве примера.
Это не проблема в C ++ 11. Увидетьstackoverflow.com/questions/8102125/…
3

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

также не яркий пример, так как такой объект может и должен бытьconstexpr
Чем отличается этот подход от OP, кроме того, что вы сделали функцию членом класса?
6

Indeed, this does work. Regrettably, you have to write globalObject().MemberFunction(), instead of globalObject.MemberFunction(), resulting in somewhat confusing and inelegant client code.

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

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

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

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

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

getGlobals().configuration.memberFunction ();

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

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

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

8

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

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

Вы правы, неразумно использовать слишком много глобальных статических переменных, но в некоторых случаях это позволяет избежать чрезмерной передачи одного и того же объекта. Подумайте об объекте регистратора, контейнере для постоянных переменных, коллекции всех соединений IPC и т. Д. Dimitri C.
«Предотвращает чрезмерную передачу объекта» это всего лишь способ сказать: «Зависимости между компонентами моей программы настолько обширны, что их отслеживание - излишняя работа. Так что лучше перестать их отслеживать. За исключением того, что обычно это не лучше - если зависимости не могут быть упрощены, то когда это так.most полезно, чтобы иметь возможность отследить их, следуя, где объект передается.
57

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

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

Но, увы, это решение не является поточно-ориентированным.
не перед основным вводом, а при первом вызове метода. увидетьblogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
@ Lightness, Хосе, Чарльз: я действительно думаю, что ваши комментарии должны быть включены в принятый ответ. В настоящее время многопоточность является обычным явлением, это может вызвать неприятные сюрпризы и не упоминается ни в одном из лучших результатов поиска для фиаско статической инициализации. то есть parashift вообще не упоминает об этом.
Потокобезопасность не является проблемой в C ++ 11. Увидетьstackoverflow.com/questions/8102125/…
@enobayram: вернуть тебя назад на 2,1 года :), да, это проблема. Весь смысл перемещения инициализации в функцию заключается в том, что инициализация теперь происходитnot доmain, но когда функция вызывается впервые, что может происходить в любой момент во время выполнения программы ... и в любом потоке.
3

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

Похожие вопросы