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

问题描述:

我想通过注册派生类的函数指针到工厂在静态映射(工厂的成员)和创建对象的工厂模式实现查找地图。但是我在这样做时遇到了分段错误。从静态成员函数访问静态映射 - 分段错误 - C++

代码段:

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.h

class Factory { 
public: 
    Factory(); 
    virtual ~Factory(); 
    static int registerCreator(int, Shape *(*CFuncptr)()); 
    Shape* CreateObject(int); 
private: 
    static std::map<int,Funcptr> funcmap; 
}; 

Square.cpp

static Shape *SquareCreator() { 
    return new Square; 
} 
static int SquareAutoRegHook = Factory::registerCreator(1,SquareCreator); 

在主创建对象工厂文件分段发生故障。 你可以请建议,如果我做错了什么。我正在使用CppUTest进行TDD,不知道如何调试。

+0

粗略地讲,不同的.cpp文件显示为'Funcptr' typedef的。你可以重写你的代码,以便在任何地方使用typedef(和测试)吗?另外,为什么'SquareCreator()'声明'静态'? –

+0

@KerrekSB:依然没有变化。 – Saaras

+0

无关紧要的是,如果函数指针总是兼容的话,我现在还不确定。我只是试图自己测试这个,而我刚刚得到了一个“无法转换”的错误。 –

无法保证静态对象的创建顺序,这些静态对象在不同的​​翻译版本中定义,因此您有50/50镜头,哪一个会先发生,Factory::funcmapFactory::registerCreator(1,SquareCreator)的初始化和未定义的行为Russian Roulette是不是一个好玩的游戏。

解决这个问题的一种常用方法,以及the third edition of Scott Meyer's Effective C++第4项中描述的方法是使用本地静态对象而不是全局静态对象。在这种情况下,它意味着改变Factory看起来像这样:

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; 
    } 
}; 

,改变Factory::registerCreator这样:

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

这样funcmap将被初始化首次registerCreator被调用,绝不会被用来初始化。

*或者,如果你不familar与术语翻译单元

+0

感谢您的回答。 – Saaras

+0

我已经尝试过这个技巧,并且遇到了一些编译器的问题,如果您在不同的静态链接库和可执行文件中使用工厂,则会创建多个funcmaps。现在我对每个库或可执行文件都有一个funcmap,但只有在某些系统上进行了编译。 – P1r4nh4

看起来像static initialization order fiasco。看起来在SquareAutoRegHook初始化时,funcmap尚未创建。

+0

如何确保初始化的顺序或任何其他干净的方式? – Saaras

+0

这看起来是正确的。奇怪的是,如果全局需要指向它的指针,则SquareCreator()具有内部链接。 –

+0

@Saaras - 你不能确保静态对象初始化的顺序,所以使用初始化例程,当从'main'调用时,这些初始化例程将为你做设置。一旦'main'启动,你可以依靠所有的静态变量来初始化(以一个未定义的顺序)。 – littleadv