C++ 从静态成员函数访问静态映射-分段错误-C++;

C++ 从静态成员函数访问静态映射-分段错误-C++;,c++,C++,我试图通过在静态映射(工厂成员)中注册派生类到工厂的函数指针并通过查找映射创建对象来实现工厂模式。但我在做这件事时犯了一个错误 代码段: factory.cpp typedef Shape* (*Funcptr)(); std::map<int,Funcptr> Factory::funcmap; int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) { Factory::funcmap[Shape

我试图通过在静态映射(工厂成员)中注册派生类到工厂的函数指针并通过查找映射创建对象来实现工厂模式。但我在做这件事时犯了一个错误

代码段:

factory.cpp

typedef Shape* (*Funcptr)();

std::map<int,Funcptr> Factory::funcmap;

int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) {
    Factory::funcmap[ShapeID] = CFuncptr;
return 1;
} 

Shape* Factory::CreateObject(int ShapeID) {
    std::map<int,Funcptr>::iterator iter;
    iter = funcmap.find(ShapeID);
    if(iter != funcmap.end()){
        return iter->second();
    }
    return NULL;
}
在主文件中为Factory创建对象时,会出现分段错误。
如果我做错了什么,你能建议一下吗。我正在为TDD使用CppUTest,不确定如何调试它。

看起来像。似乎在初始化
SquareAutoRegHook
时,
funcmap
尚未创建。

无法保证在不同的翻译UINT*中定义的静态对象的创建顺序,因此您有一个50/50的机会来确定哪个将首先发生,初始化
Factory::funcmap
Factory::registerCreator(1,SquareCreator)
和未定义的行为俄罗斯轮盘赌不是一个好游戏

处理此问题的常用方法,以及的第4项中所述的方法,是使用局部静态对象而不是全局静态对象。在这种情况下,它意味着将
工厂更改为如下所示:

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> & GetFactoryMap() {
        static std::map<int,Funcptr> funcmap;
        return funcmap;
    } 
}; 
这样,funcmap将在第一次调用registerCreator时初始化,并且在未初始化的情况下永远不会使用


*或者,粗略地说,如果您不熟悉术语翻译单元,那么不同的.cpp文件将显示
Funcptr
的typedef。你能重写你的代码,让它在任何地方都使用typedef(和test)吗?另外,为什么
SquareCreator()
声明为
static
。我只是试着自己测试一下,结果得到了一个“无法转换”的错误。@KerrekSB:对不起,我不明白什么是“协变兼容”。我能够编译代码。实际上,我希望创建一个没有if-else语句的工厂,这样它就不会违反OCP。所以我尝试使用函数指针。另外,我不想创建所有派生类对象并将其注册到映射中。如果你能给我建议一些其他干净利落的方法,这将是很有帮助的;B类:公共A类
,那么问题是
B*(*)()
是否可转换为
A*(*)()
。我想是的,但我的编译器告诉我这是一个错误…哦,好吧,我现在明白了,但对我来说,它没有抛出任何错误。我使用的是gcc 4.5.3。您是否可以提供任何示例实现链接/或我上面提到的创建对象的工厂概念?如何确保初始化的顺序或任何其他整洁的方式?这看起来是正确的。如果全局需要指向它的指针,
SquareCreator()
具有内部链接,这也很奇怪。@Saaras-您无法确保静态对象的初始化顺序,因此使用初始化例程,当从
main
调用时,这些例程将为您进行设置。一旦
main
启动,您就可以依赖所有要初始化的静态变量(以未定义的顺序)。我尝试过这个技巧,但我遇到了一个问题,即在某些编译器中,如果跨不同的静态链接库和可执行文件使用工厂,就会创建多个Funcmap。现在我为每个库或可执行文件都有一个funcmap,但前提是它是在某些系统上编译的。
static Shape *SquareCreator() { 
    return new Square; 
}
static int SquareAutoRegHook = Factory::registerCreator(1,SquareCreator);
class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> & GetFactoryMap() {
        static std::map<int,Funcptr> funcmap;
        return funcmap;
    } 
}; 
int Factory::registerCreator(int ShapeID, Shape *(*CFuncptr)()) {   
    GetFactoryMap()[ShapeID] = CFuncptr;   
    return 1;   
}