#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
, она отлично работает.