неділя, 4 лютого 2018 р.

LazyInstance

В C++ глобальные объекты не POD типов имеют динамические инициализаторы. Это означает, что при запуске приложения до вызова функции main произойдет инициализация этих глобальных объектов. Это, конечно, влияет на время старта приложения. Но самое страшное, что эта инициализация и, соответственно, деинициализация глобальных объектов в разных единицах трансляции происходят недетерминированно. То есть в стандарте языка не указывается, в каком порядке будут вызываться конструкторы и деструкторы объектов. Как правило, это зависит от порядка линковки объектных файлов. Что это влечет за собой? А то, что если вы в одном файле создаете глобальный объект и инициализируете его значением другого глобального объекта в другой единице трансляции или при закрытии приложения начинаете обращаться к таким глобальным объектам, результат непредсказуем. Чаще всего возникает краш.

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

Во вторых, можно пойти путем Chromium и использовать шаблонный класс LazyInstance:

// Destructor never gets called.
 LazyInstance<std::string>::Leaky g_string = LAZY_INSTANCE_INITIALIZER;

 // Destructor gets called on program exit.
 LazyInstance<std::string>::DestructorAtExit g_string2 = LAZY_INSTANCE_INITIALIZER;

 void Foo() {
   g_string.Get() = "Hello";
   g_string2.Get() = "World";
 }

Этот класс при вызове метода Get() производит потокобезопасную инициализацию внутреннего объекта. И также можно указать, нужно ли вызвать деструктор в момент закрытия программы или нет. Так же стоит отметить, что LazyInstance не использует динамическую память для уменьшения фрагментации кучи.

HyperComments for Blogger

comments powered by HyperComments