Предотвращение фиаско с порядком статической инициализации, C ++

Однажды я прочитал замечательный FAQ по C ++ (это действительно хорошо !!) и прочитал тема о том, как предотвратить" фиаско "статического порядка инициализации. Поэтому автор советует обернуть статические переменные в функции, чтобы предотвратить «фиаско», сохранив порядок создания переменных. Но мне кажется, что это грубый обходной путь. Итак, мой вопрос: есть ли какой-нибудь современный, более ориентированный на шаблоны способ предотвратить это "фиаско", кроме как обернуть "статический материал" в функции ???


person Eduard Rostomyan    schedule 23.04.2015    source источник
comment
Элегантный способ предотвратить фиаско - никогда не использовать статические объекты, которые ни от чего не зависят.   -  person eerorika    schedule 23.04.2015
comment
В FAQ рассказывается об идиоме "Конструировать при первом использовании". Знакомый шаблон для многих программистов на C ++. Его просто реализовать и еще проще использовать. Я не понимаю, что вы подразумеваете под современным, более ориентированным на шаблоны способом.   -  person Cássio Renan    schedule 23.04.2015
comment
См. Также AddressSanitizerInitializationOrderFiasco.   -  person jww    schedule 11.01.2016


Ответы (3)


Современный, более ориентированный на шаблоны способ - это вообще не использовать глобальные переменные.

Другого пути нет.

Иначе это не было бы большим фиаско!

person Lightness Races in Orbit    schedule 23.04.2015
comment
К сожалению, это нереально. Это не помогает тысячам пакетов в типичном репозитории или FTP-сайте GNU. - person jww; 11.01.2016
comment
@jww: Единственная разумная альтернатива - делать то, что предлагают сами ОП в вопросе. - person Lightness Races in Orbit; 11.01.2016
comment
Это ошибочный выбор. Это перемещает проблему, так что сбой происходит в деструкторе; а не конструктор. Я на собственном опыте испытал сбой в dtor, когда объект исчезает слишком быстро. - person jww; 11.01.2016
comment
@jww Код с этой проблемой имеет серьезные проблемы взаимозависимости и требует рефакторинга. - person Lightness Races in Orbit; 11.01.2016

Итак, мой вопрос: есть ли какой-нибудь современный, более ориентированный на шаблоны способ предотвратить это "фиаско", кроме как обернуть "статический материал" в функции ???

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

На практике могут возникнуть ситуации, когда требуются статические данные. Если нет зависимостей от других статик, сделайте статические данные const/constexpr.

// smart pointer that implements the "Foo" release policy
class FooPointer
{
    static const FooPointer NullFoo; // does not depend on other static values
    /* ... */
};

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

// smart pointer that implements the "Foo" release policy
class FooPointer
{
    static const FooPointer& NullFoo(); // depends on other static values
    /* ... */
};

Обобщить:

Большинство (90% - 99%?) Статических / глобальных / общих данных должны вводиться в зависимости от того, где они используются, а не создаваться как статические вообще.

В редких случаях, когда статика требуется по той или иной причине и она не зависит от другой статики, объявляйте статические переменные.

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

Как показывает практика, если у вас много второго и третьего случаев, вы не делаете достаточно первого.

person utnapistim    schedule 23.04.2015

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

Затем постройте объекты в нужном порядке. Например, если у нас есть два объекта x и y, и построение y завершится неудачно, если x не был построен, сначала создайте x и передайте его конструктору (или другому члену) y)

 SomeObject x;
 SomeOtherObject y(x);

or

 SomeObject *x = new SomeObject;
 SomeOtherObject y = new SomeObject(*x);   

(оба из вышеперечисленных предполагают, что конструктору y требуется ссылка).

Если вам нужно разделить x и y между функциями, просто передайте их функциям в качестве аргументов.

Если вы должны использовать статику (т.е. вы не хотите, чтобы вводились передаваемые аргументы повсюду), сделайте статику указателями и инициализируйте их один раз (например, в main()).

//  all source files can use x and y via these declarations  (e.g. via a header file)

extern SomeObject *x;
extern SomeOtherObject *y;

//  definition in one source file only

SomeObject *x;
SomeOtherObject *y;

int main()
{
     x = new SomeObject;
     y = new SomeOtherObject(*x);

       // call other functions that use x and y.

     delete y;
     delete x;
}

Но на самом деле лучше избегать использования статики, если это вообще возможно.

person Peter    schedule 23.04.2015
comment
+1 Это лучший подход, чем конструкция при первом использовании идиомы ИМХО. Возможное улучшение при масштабировании до более крупных проектов - использование функций инициализации и доступа (чтобы скрыть указатель и подтвердить, что инициализация была выполнена) и группировка инициализаций по библиотеке. Однако основная идея та же: взять под контроль, когда будет завершена инициализация и очистка, и не оставлять это на волю случая. - person markh44; 28.04.2020