"1) Если я хочу воспроизвести эту проблему, как я могу это сделать (чтобы моя программа вылетала)? Я хотел бы написать тестовую программу для воспроизведения сбоя. Можете ли вы предоставить исходный код, если это возможно ? "
Вы не можете написать переносной тестовый набор. Порядок статической инициализации фиаско в том, что порядок не определен. Проблема возникает, когда кто-то пишет код, который будет работать, если он инициализирован в одном законном порядке, но не будет работать, если он инициализирован в другом законном порядке. Таким образом, по той же причине, по которой вы не можете гарантировать, что она будет работать, вы не можете гарантировать, что она потерпит неудачу. Вот и весь смысл.
Возможно, вы могли бы предположить, что компоновщик инициализирует все глобальные переменные из одной единицы перевода перед всеми глобальными переменными из другого. Поэтому настройте два исходных файла A и B, с глобальными переменными A1 и A2 в A и B1 и B2 в B. Затем используйте B1 (под этим я подразумеваю «сделать что-то, что не получится, если B1 не был инициализирован») в конструкторе A1, и используйте A2 в конструкторе B2. Также используйте A1 в конструкторе A2 (и объявите их в том порядке в A). Тогда единственный порядок, который не потерпит неудачу, это B1, A1, A2, B2, который вы можете себе представить - это довольно маловероятный выбор для реализации. В конкретной реализации, если это как-то удастся, переключите вещи так, чтобы A2 использовал B2 вместо B2, используя A2, и просто надеемся, что это не изменит порядок инициализации.
Конечно, вы также можете использовать B2 в конструкторе B1 (и объявить их в этом порядке в B), чтобы гарантировать сбой независимо от порядка инициализации. Но тогда это не будет фиаско статического порядка инициализации, это будет просто принципиально нарушенная циклическая зависимость.
"2) Я прочитал эту статью C ++ FAQ Lite, в которой говорится, что у него два статических объекта, x и y, в двух разных файлах, а y вызывает метод x. Как это возможно, поскольку глобальные статические члены имеют область действия на уровне файлов?"
Например, объявите их extern
в обеих единицах перевода (возможно, используя общий заголовок). Объем, связь и продолжительность хранения - разные вещи.
"3) Эта проблема очень опасна, есть ли попытки исправить ее на уровне компилятора?"
Не то, что я знаю. Я почти уверен, что проблема заключается в том, чтобы определить, использует ли объект X (в том смысле, который я определил выше) объект Y в своем конструкторе, поэтому построение графа зависимостей во время ссылки и t-сортировка будут Лучшая частичная мера.
"4) Сколько раз вы, специалисты по C ++, сталкивались с этой проблемой в реальной жизни?"
Никогда, потому что (а) я не оставляю глобалы валяться и (б) там, где я их использовал, я избегал делать что-либо причудливое в их инициализаторах. По сути, не проектируйте класс, а затем решите иметь его глобальный экземпляр - если вы собираетесь использовать глобальный объект, проектируйте его как глобальный. И везде, где это возможно, используйте статические данные локального уровня, а не глобальные. Если вам нужно предложить нечто, похожее на глобальное, опубликуйте его как функцию, которая возвращает ссылку на объект, или как объект, который они могут создать в своем стеке и который вызывает эту функцию для них, а затем действует как прокси (или обрабатывать, если хотите) для глобального состояния. Вам по-прежнему приходится беспокоиться о безопасности потоков, но многопоточные среды предоставляют способы управления этим, в то время как нет возможности управлять фиаско, кроме как заставить ваших абонентов определять ваши глобальные переменные в их модуле перевода.
Это становится трудным, только если вы реализуете API, который определяет глобальные переменные, такие как std::out
. Есть трюк, который вы можете использовать, когда вы определяете фиктивную переменную области файла в том же заголовке, который объявляет глобальный. Хотя я не могу вспомнить имя.