初始化前使用的静态映射

时间:2018-10-16 09:07:27

标签: c++ static std

我正在尝试实现this之类的工厂模式。

现在的问题是,由于映射尚未初始化,程序在注册函数中以段错误终止。

// initialise the registered names map
std::map<std::string, factoryMethod> SourceFactory::registeredClasses_ = { };

bool SourceFactory::Register(std::string name, factoryMethod createMethod) {
    // registeredClasses_ = { }; // This prevents the segfault but does not work for obvious reasons
    auto temp = std::make_pair(name.c_str(), createMethod);

    std::pair<std::map<std::string, factoryMethod>::iterator, bool> registeredPair =
            SourceFactory::registeredClasses_.insert(temp);

    return registeredPair.second;
}

为什么在初始化地图之前可以调用Register()?我尝试在头文件中初始化地图,但随后出现链接器错误

  

SourceFactory :: registeredClasses_的多个定义

一个解决方案是设置一个静态布尔isInitialized=false,然后相应地初始化映射。但我希望可以避免。

2 个答案:

答案 0 :(得分:1)

可能是在初始化注册表之前从另一个翻译单元调用Register的情况。
不幸的是,添加静态标志不会解决任何问题,因为这也不会被初始化。

一个方便的解决方案是添加一个间接级别:

// static
std::map<std::string, factoryMethod>& SourceFactory::registry()
{
    static std::map<std::string, factoryMethod> registeredClasses;
    return registeredClasses;
}

bool SourceFactory::Register(const std::string& name, factoryMethod createMethod) {
    auto temp = std::make_pair(name, createMethod);
    return registry().insert(temp).second;
}

答案 1 :(得分:0)

这是一个常见的问题,称为static initialisation order fiasco

在命名空间范围内具有静态存储持续时间的对象是按顺序在翻译单元中构造的,但是您不知道其他翻译单元中的对象是否可能首先进行初始化。

不要将您的容器置于命名空间范围内,而是使其成为 function静态(也许是从新的GetRegistry()函数返回的?),以便在首次使用时对其进行构造。可以从main开始使用,从另一个静态持续时间“事物”的初始化开始使用(大概是您的Register呼叫来自的地方),从月球开始使用...

这也是为什么编写单例的正确方法是拥有一个GetInstance()函数来声明(static ly!)实例函数范围内。 / p>

  

一个解决方案是设置一个静态布尔isInitialized = false,然后相应地初始化映射。但我希望可以避免。

不。这样,您不仅会遇到isInitialized标志的相同问题,而且您无法对该信息进行任何“处理”。除了初始化器之外,您无法“初始化”其他东西,整个问题是尚未使用初始化器。您可以分配给地图,但这将具有未定义的行为,因为您将分配给尚不存在的东西……然后无论如何它将被初始化!