Tengo un archivo: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
y otro archivo: BaseFactory.h
#include "Base.h"
class BaseFactory
{
public:
BaseFactory(const string &sClassName){msClassName = sClassName;};
Base * Create()
{
if(msClassName == "DerivedA")
{
return new DerivedA();
}
else if(msClassName == "DerivedB")
{
return new DerivedB();
}
else if(/*etc...*/)
{
/*etc...*/
}
};
private:
string msClassName;
};
/*etc.*/
¿Hay alguna manera de convertir de alguna manera esta cadena en un tipo real (clase), para que BaseFactory no tenga que conocer todas las clases derivadas posibles y tener if () para cada una de ellas? ¿Puedo producir una clase a partir de esta cadena?
Creo que esto se puede hacer en C # a través de Reflection. ¿Hay algo similar en C ++?
c++
inheritance
factory
instantiation
Gal Goldman
fuente
fuente
Respuestas:
No, no hay ninguno, a menos que hagas el mapeo tú mismo. C ++ no tiene ningún mecanismo para crear objetos cuyos tipos se determinen en tiempo de ejecución. Sin embargo, puede usar un mapa para hacer ese mapeo usted mismo:
Y luego puedes hacer
Obteniendo una nueva instancia. Otra idea es que los tipos se registren a sí mismos:
Podría decidir crear una macro para el registro
Sin embargo, estoy seguro de que hay mejores nombres para esos dos. Otra cosa que probablemente tiene sentido usar aquí es
shared_ptr
.Si tiene un conjunto de tipos no relacionados que no tienen una clase base común, puede darle al puntero de función un tipo de retorno
boost::variant<A, B, C, D, ...>
. Como si tuvieras una clase Foo, Bar y Baz, se ve así:A
boost::variant
es como una unión. Sabe qué tipo está almacenado en él mirando qué objeto se usó para inicializar o asignarle. Echa un vistazo a su documentación aquí . Finalmente, el uso de un puntero de función sin formato también es un poco antiguo. El código C ++ moderno debe estar desacoplado de funciones / tipos específicos. Es posible que desee investigarBoost.Function
para buscar una mejor manera. Entonces se vería así (el mapa):std::function
también estará disponible en la próxima versión de C ++, incluidostd::shared_ptr
.fuente
DerivedB::reg
realmente se inicialice? Según tengo entendido, es posible que no se construya en absoluto si no se define ninguna función u objeto en la unidad de traducciónderivedb.cpp
, según 3.6.2.BaseFactory::map_type * BaseFactory::map = NULL;
en mi archivo cpp. Sin esto, el enlazador se quejó de un mapa de símbolos desconocido.DerivedB::reg
no se inicializa si ninguna de sus funciones o instancias está definida en la unidad de traducciónderivedb.cpp
. Eso significa que la clase no se registra hasta que realmente se instancia. ¿Alguien sabe una solución para eso?No, no lo hay. Mi solución preferida para este problema es crear un diccionario que asigne el nombre al método de creación. Las clases que desean crearse así registran un método de creación con el diccionario. Esto se discute con cierto detalle en el libro de patrones GoF .
fuente
La respuesta corta es que no puedes. Vea estas preguntas SO por qué:
fuente
He respondido en otra pregunta SO sobre las fábricas de C ++. Vea allí si una fábrica flexible es de interés. Intento describir una forma antigua de ET ++ para usar macros que me ha funcionado muy bien.
ET ++ fue un proyecto para portar MacApp antiguo a C ++ y X11. En el esfuerzo, Eric Gamma, etc., comenzó a pensar en patrones de diseño.
fuente
boost :: funcional tiene una plantilla de fábrica que es bastante flexible: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Sin embargo, mi preferencia es generar clases de contenedor que oculten el mapeo y el mecanismo de creación de objetos. El escenario común que encuentro es la necesidad de asignar diferentes clases derivadas de alguna clase base a claves, donde todas las clases derivadas tienen una firma de constructor común disponible. Aquí está la solución que se me ocurrió hasta ahora.
En general, me opongo al uso intensivo de macros, pero he hecho una excepción aquí. El código anterior genera GENERIC_FACTORY_MAX_ARITY + 1 versiones de una clase llamada GenericFactory_N, para cada N entre 0 y GENERIC_FACTORY_MAX_ARITY inclusive.
Usar las plantillas de clase generadas es fácil. Suponga que desea que una fábrica cree objetos derivados de BaseClass utilizando una asignación de cadena. Cada uno de los objetos derivados toma 3 enteros como parámetros de constructor.
El destructor de clase GenericFactory_N es virtual para permitir lo siguiente.
Tenga en cuenta que esta línea de la macro genérica del generador de fábrica
Asume que el archivo de encabezado genérico de fábrica se denomina GenericFactory.hpp
fuente
Solución detallada para registrar los objetos y acceder a ellos con nombres de cadena.
common.h
:test1.h
:main.cpp
:Compilar y ejecutar (he hecho esto con Eclipse)
Salida:
fuente
Significa reflexión como en Java. Hay alguna información aquí: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
En términos generales, busque en google "c ++ reflexión"
fuente
Tor Brede Vekterli proporciona una extensión de impulso que le brinda exactamente la funcionalidad que busca. Actualmente, es un poco incómodo con las bibliotecas de impulso actuales, pero pude hacerlo funcionar con 1.48_0 después de cambiar su espacio de nombres base.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
En respuesta a aquellos que se preguntan por qué tal cosa (como reflexión) sería útil para c ++. y produce un objeto del tipo deseado.
El principal beneficio de usar el marco aquí (sobre el mantenimiento de una lista de frutas en alguna parte) es que la función de registro está en la definición de cada clase (y solo requiere una línea de código que llame a la función de registro por clase registrada), en lugar de un archivo que contiene la lista de frutas, que debe agregarse manualmente cada vez que se deriva una nueva clase.
Hice de la fábrica un miembro estático de mi clase base.
fuente
Este es el patrón de fábrica. Ver wikipedia (y este ejemplo). No puede crear un tipo per se desde una cadena sin algún hack atroz. ¿Por qué necesitas esto?
fuente