Могу ли я полагаться на статическую переменную в области функций для метода, вызываемого во время завершения работы программы? - PullRequest
0 голосов
/ 22 октября 2018

Краткий контекст: я вижу ошибки при завершении работы программы, которые происходят из-за зависимостей между глобальными членами ( :: sigh :: , я знаю, я знаю).Деструктор одной глобальной переменной может ссылаться на другой глобал - и если тот уже разрушен, все становится плохо.

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

Вот игрушечный пример, демонстрирующий, что меня интересует:

class Logger
{
public:
    enum class Severity { DEBUG, INFO, WARNING, ERROR };
    void Log(Severity sev, const std::string& msg)
    {
        LogImpl(FormatMessage(sev, msg));
    }

    Logger() { Log(Severity::INFO, "Logger active"); }
    ~Logger() { Log(Severity::INFO, "Logger inactive"); }

protected:
    static std::string FormatMessage(Severity sev, const std::string& msg)
    {
        static const std::map<Severity, std::string> enum2str {
            {Severity::DEBUG, "DEBUG"},
            {Severity::INFO, "INFO"},
            {Severity::WARNING, "WARNING"},
            {Severity::ERROR, "ERROR"}
        };

        // Throws or crashes if enum2str is invalid, or uninitialized:
        return "[" + enum2str[sev] + "] " + msg;
    }
    void LogImpl(const std::string& msg)
    {
        std::cout << msg << std::endl;
    }
};

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

По стандартному это может вызвать сбой моей программы при завершении работы? Не является ли enum2str ненадежным по своей природе во время выключения?Или есть некоторая обработка этого - например, если enum2str недействителен в какой-то момент, возможно, будет создан новый статический экземпляр?

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

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

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

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

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

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

Вы не можете полагаться на него в целом, но вы можете положиться на него при определенных обстоятельствах.

На atExit безопасно полагаться на статические объекты, поэтомубезопасно звонить FormatMessage там.Если вы не можете гарантировать порядок разрушения между определенным статическим объектом s и enum2str, использование FormatMessage в деструкторе s небезопасно.

Статические объекты гарантированно будутуничтожены в обратном порядке их постройки.Следовательно, вы можете положиться на enum2str, существующий при уничтожении подмножества статических объектов, чьи конструкторы вызывают FormatMessage, потому что вызов FormatMessage в конструкторе гарантирует, что enum2str будет построен до того, как этот зависимый статический объект завершит построение.

Существует хитрость в том, чтобы полагаться на статический объект независимо от порядка уничтожения: никогда не разрушайте зависимого.Это может быть достигнуто с помощью статического указателя области действия на динамически размещенный объект, который вы намеренно никогда не удаляете.Как недостаток, это вызовет диагностику в анализаторах памяти и может повысить кровяное давление ваших догматических коллег.

0 голосов
/ 22 октября 2018

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

[basic.start.term]

3Если завершение конструктора или динамическая инициализация объекта со статической продолжительностью хранения сильно происходит раньше, чем у другого, завершение деструктора второго секвенируется до инициации деструктора первого [...]

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

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

...