2011-11-08 7 views
5

Estoy tratando de implementar un patrón de fábrica registrando los punteros de función de la clase derivada a la fábrica en un mapa estático (miembro de la fábrica) y creando objetos por mirando el mapa Pero estoy obteniendo un error de segmentación al hacer esto.accediendo a un mapa estático de una función de miembro estático - falla de segmentación - C++

Fragmento de código:

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

En la creación del objeto de fábrica en el principal archivar un segmentati en falla ocurre. ¿Puede sugerir si estoy haciendo algo mal? Estoy usando CppUTest para TDD y no estoy seguro de cómo depurar esto.

+0

tenemos los typedef para 'Funcptr'. ¿Puedes reescribir tu código para que use el typedef en todas partes (y prueba)? Además, ¿por qué 'SquareCreator()' declaró 'static'? –

+0

@KerrekSB: todavía no hay cambios. – Saaras

+0

Sin conexión, ni siquiera estoy seguro ahora si los punteros a función son covariantemente compatibles. Intenté probarlo yo mismo, y obtuve un error rotundo de "no puedo convertir". –

Respuesta

7

No hay ninguna garantía sobre el orden de creación de objetos estáticos que se definen en diferentes traducciones uints * por lo que tiene un tiro 50/50 en cuanto a qué pasaría en primer lugar, la initializtion de Factory::funcmap o Factory::registerCreator(1,SquareCreator) y un comportamiento indefinido ruleta rusa no es un buen juego para jugar.

Un enfoque común para hacer frente a esto, y uno que se describe en el artículo 4 de the third edition of Scott Meyer's Effective C++ es utilizar objetos estáticos locales en lugar de objetos estáticos globales. En este caso, significa cambiar Factory a tener este aspecto:

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

y cambiando Factory::registerCreator a esto:

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

Este funcmap manera se iniciará la primera vez registerCreator se llama y nunca será utilizado sin inicializar .

* O, en términos generales, los diferentes archivos .cpp si no está familiarizado con el término unidad de traducción

+0

Gracias por la respuesta. – Saaras

+0

He intentado este truco y he tenido el problema de que con algunos compiladores se crean múltiples mapas de funciones si utiliza la fábrica en diferentes bibliotecas y ejecutables vinculados estáticamente. Ahora tengo un mapa func para cada biblioteca o ejecutable, pero solo si está compilado en ciertos sistemas. – P1r4nh4

0

Se parece al static initialization order fiasco. Parece que en el momento en que se inicializó SquareAutoRegHook, el funcmap no se ha creado aún.

+0

¿Cómo asegurar el orden de inicialización o cualquier otra manera ordenada de hacerlo? – Saaras

+0

Eso se ve bien. También es extraño que 'SquareCreator()' tenga un enlace interno si se requiere un puntero a él de forma global. –

+0

@Saaras: no se puede garantizar el orden de inicialización de los objetos estáticos, por lo tanto, use las rutinas de inicialización que le harán la configuración cuando se llame desde 'main'. Una vez que 'main' comienza puede confiar en todas las variables estáticas que se inicializarán (en un orden indefinido). – littleadv

Cuestiones relacionadas