Неинициализированная глобальная переменная на карте внутри метода класса

#c #constructor #global-variables

Вопрос:

У меня есть следующая проблема, у меня есть класс, который я хочу построить из двух разных видов сложных объектов. Внутри метода класса есть карта, которая строится из extern переменных, которые я определил во внешней части класса. Если я хочу объявить объект класса внутри основной функции, все идет нормально. Но если я объявляю его вне main, конструктор класса вызывается до инициализации внешних объектов, из которых я построил внутреннюю карту в методе класса. Ошибка повторяется на Clang и MSVC. Если я использую простые объекты для построения карты (например int или double ), все работает нормально в обоих направлениях.

main.cpp

 #include "TestClass.h"

TestClass test1 = TestClass(g_Spec1); // 1

int main()
{
    TestClass test2 = TestClass(g_Spec1); // 2
}
 

Если вы прокомментируете 1, все работает нормально, однако, как есть, 2 не видел внешних переменных.

Тестовый класс.h

 #pragma once
#include <iostream>
#include <map>

#include "Token.h"

extern const Spec_t g_Spec1;
extern const Spec_t g_Spec2;
extern const Spec_t g_Spec3;

extern const Name_t g_Name1;
extern const Name_t g_Name2;
extern const Name_t g_Name3;

class TestClass {
private:
    Name_t name;
    Spec_t spec;

    static const Name_tamp; foo(const Spec_tamp; j);

public:
    TestClass() = defau<
    TestClass(const Spec_tamp; j);

    virtual ~TestClass() = defau<
};
 

TestClass.cpp

 #include "TestClass.h"

const Spec_t g_Spec1{ {Token::Type1, 2} };
const Spec_t g_Spec2{ {Token::Type1, 3} };
const Spec_t g_Spec3{ {Token::Type1, 4} };

const Name_t g_Name1{ "Name1" };
const Name_t g_Name2{ "Name2"};
const Name_t g_Name3{ "Name3" };

const Name_tamp; TestClass::foo(const Spec_tamp; spec_)
{
    static const std::map<Spec_t, Name_t > glmap1 =
    {
        {g_Spec1, g_Name1},
        {g_Spec2, g_Name2}
    };

    std::cout << "Foo: Map size must be 2, but it is " << glmap1.size() << std::endl;
    std::cout << "In FOO Name1 is " << g_Name1 << std::endl;

    auto constIterator = glmap1.find(spec_);
    if (constIterator == glmap1.cend())
    {
        std::cout << "Not found " << std::endl; // 3
        return g_Name3;
    }
    else
    {
        std::cout << "Found " << constIterator->second << std::endl; // 4
        return constIterator->second;
    }
}

TestClass::TestClass(const Spec_tamp; j)
{
    name = foo(j);
    spec = j;
    std::cout << "In CONSTRUCTOR name as global variable from map " << name << std::endl;
}
 

Еще одна интересная вещь заключается в том, что 1 вызов 4(найден) и 2 вызова 3(Не найден), но карта пуста в обоих направлениях. Может быть, это потому g_Spec1 , что не инициализировался в это время?
И я не понял, почему карта внутри отладчика пуста, но у size = 1

Жетон.h

 #pragma once
#include <cinttypes>
#include <string>
#include <vector>

enum class Token
{
    Type1,
    Type2
 };

struct TokenId
{
    Token tok;

    std::uint64_t val;

    bool operator==(const TokenIdamp; p_Other) const noexcept
    {
        return (tok == p_Other.tok amp;amp; val == p_Other.val);
    }

    bool operator<(const TokenIdamp; p_Other) const noexcept
    {
        if (tok < p_Other.tok) return true;
        else if (tok == p_Other.tok)
        {
            return val < p_Other.val;
        }
        else return false;
    }
};

using Name_t = std::string;
using Spec_t = std::vector<TokenId>;
 

Ответ №1:

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

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

Это лишь одна из причин, по которой лучше избегать глобальных переменных. В вашем случае просто поместите эту переменную внутрь main() .

Комментарии:

1. Спасибо. Я понимаю, что это похоже на фиаско со статическим порядком инициализации , но почему все работает нормально, когда карта строится не из <Spec_t, Name_t> , а из <double, int> ? Я могу понять, что оптимизация может помещать переменные внутри карты, как при использовании встроенного или constexpr, но в режиме отладки оптимизация отсутствует. И почему инициализация глобальных переменных не происходит, когда я начинаю выполнять main. Еще один вопрос: почему, если я оставил только инициализацию в main и удалил статику с карты, я получаю ошибку длины?

2. «Отлично работает» может быть совпадением; я не видел, какие значения вы использовали для инициализации double и int . Обратите внимание, что эти глобальные значения будут иметь значения 0.0 и 0 соответственно до правильной инициализации;

3. Что касается удаления static , то вы возвращаете ссылку на локальную переменную, срок службы которой истек. Карта уничтожается до того, как вы сможете ее использовать.

4. static Проблемы Благодарности кажутся логичными.

5. Для double и int Я использовал следующий код. TestClass.cpp pastebin.com/6MJVjNc4 Тестовый класс.h pastebin.com/MYtVggzm и даже если карты нет static , она отлично работает.