2012-01-02 19 views
6

Código:¿Cómo crear una matriz de clases en C++?

struct Base { ... }; 

struct A : public Base { ... }; 
struct B : public Base { ... }; 
struct C : public Base { ... }; 

¿Es posible crear una matriz, que sostiene que los tipos de estructura? muestra/resultado esperado:

Type inheritedTypesOfStruct[3] = {A, B, C}; 

El propósito de esto es que yo quiero más adelante para crear un objeto con una clase al azar recuperado de la matriz.

+0

¿Quieres decir que: 'vector v ;' –

+3

¿Quieres una matriz de los mismos tipos? O una matriz de objetos de los tipos? –

+1

@BenjaminLindley: Obviamente él quiere una variedad de tipos. Sin embargo, el uso de 'Base' como tipo de elemento de matriz es engañoso. –

Respuesta

1
#include <cstdlib> 
#include <ctime> 
#include <iostream> 
#include <map> 
#include <vector> 
#include <memory> 

using namespace std; 




// interface 
class Base 
{ 
public: 
    virtual ~Base() { } 
    virtual int getClassId() = 0; 
}; 


// class A relizes interface Base, has ID == 1 (is used in automatic registration to factory) 
class A : public Base 
{ 
public: 
    const static int ID = 1; 
    static Base* CreateInstance() 
    { 
     return new A(); 
    } 

    virtual int getClassId() 
    { 
     return ID; 
    } 

    virtual ~A() { } 
}; 


// class B relizes interface Base, has ID == 2 (is used in automatic registration to factory) 
class B : public Base 
{ 
public: 
    const static int ID = 2; 
    static Base* CreateInstance() 
    { 
     return new B(); 
    } 

    virtual int getClassId() 
    { 
     return ID; 
    } 

    virtual ~B() { } 
}; 



// this is the objects factory, with registration only (unregister s not allowed) 
class ObjectFactory 
{ 
    ObjectFactory() { } 
    ObjectFactory(ObjectFactory&) { } 
public: 
    virtual ~ObjectFactory() { } 
    static ObjectFactory& instance() 
    { 
     static ObjectFactory objectFactory; 

     return objectFactory; 
    } 

    typedef Base* (*Creator)(); 

    void registerCreator(int id, Creator creator) 
    { 
     registry[id] = creator; 
    } 

    Base* CreateById(int id) 
    { 
     return registry[id](); 
    } 

private: 
    map<int, Creator> registry; 
}; 


// this template class is used for automatic registration of object's creators 
template <class T> 
struct RegisterToFactory 
{ 
    RegisterToFactory(ObjectFactory& factory) 
    { 
     factory.registerCreator(T::ID, &T::CreateInstance); 
    } 
}; 


namespace 
{ 
    // automaticaly register creators for each class 
    RegisterToFactory<A> autoregisterACreator(ObjectFactory::instance()); 
    RegisterToFactory<B> autoregisterBCreator(ObjectFactory::instance()); 
} 




// lets this this solution 
int main(int argc, char *argv[]) 
{ 
    vector<int> ids; 

    ids.push_back(static_cast<int>(A::ID)); 
    ids.push_back(static_cast<int>(B::ID)); 

    srand(time(0)); 

    for (int i = 0; i < 20; ++i) 
    { 
     int randomClasssId = ids[rand() % ids.size()]; 
     auto_ptr<Base> testObject(ObjectFactory::instance().CreateById(randomClasssId)); 
     cout << "Object of classId = " << testObject->getClassId() << " has been produced by factory." << endl; 
    } 


    system("PAUSE"); 
    return EXIT_SUCCESS; 
} 
+0

Si quiere proteger el creador de objetos, mueva el creador estático a la sección privada/protegida, y conviértase en una clase amiga para una clase de creador externa concreta. Esto puede ser útil siempre que el proceso de creación sea complejo y propenso a errores. El creador externo tiene conocimiento completo de cómo crear una instancia adecuada de los objetos de esta clase, por lo que el usuario final nunca falla aquí. – rzur2004

+0

No recomiendo usar RTTI a menos que sepa la plataforma concreta, y cada módulo (DLL, SO, LIB ....) tiene estrictamente la misma opción de compilación y enlace. Algunos compiladores más antiguos pueden tener problemas con RTTI con espacios de nombres y registros de módulos cruzados en tales fábricas, especialmente cuando intentamos registrar clases de plantillas. En general, RTTI es una semana, intente no usarlo, ya que RTTI puede deshabilitarse en la configuración del edificio y no se ejecutará. – rzur2004

+0

Ok, lo intento, thx – justi

0

No entiendo la pregunta. ¿Estás pidiendo una matriz que pueda contener diferentes tipos de instancias al mismo tiempo? Eso es posible usando polimorfismo, por supuesto. ¿O estás tratando de obtener una variedad de tipos (como la reflexión)? Eso sería posible usando la información del tipo RTTI o Qt (como ejemplo), pero nunca lo hice.

+0

Las solicitudes de aclaración van como comentarios, no como respuestas. –

5

Si su compilador soporta RTTI, se puede hacer algo como:

const type_info *inheritedTypesOfStruct[3] = { 
    &typeid(A), &typeid(B), &typeid(C) 
}; 

Sin embargo, usted no será capaz de crear una instancia de una clase usando sólo su type_info. El factory pattern podría ser una mejor respuesta a su problema de raíz.

Actualización: Desde type_info casos no se pueden copiar (su constructor de copia y operador de asignación son private), y matrices de referencias son ilegales, punteros constantes tienen que ser utilizado en el ejemplo anterior.

5

Puede crear una matriz de funciones, cada una de las cuales devuelve un puntero base (o puntero inteligente) que cada punto a los objetos de sus diversas clases derivadas. p.ej.

typedef std::unique_ptr<Base> base_ptr; 

template<typename Derived> 
base_ptr CreateObject() 
{ 
    return base_ptr(new Derived); 
} 

int main() 
{ 
    std::function<base_ptr(void)> f[3] = { 
     CreateObject<A>, CreateObject<B>, CreateObject<C> 
    }; 

    base_ptr arr[10]; 
    for (int i=0; i<10; ++i) 
     arr[i] = f[rand()%3](); 
} 

Aquí está en acción: http://ideone.com/dg4uq

+1

Hm, interesante .. – justi

+0

Intento Tu ejemplo, pero en mi compilador no funciona: hay algún problema con unique_ptr – justi

+1

@justi: Prueba incluir el encabezado ''. Debería haber hecho eso de todos modos, sucedió que uno de los otros encabezados aparentemente lo trajo. Si eso no funciona, ¿qué compilador (y versión) estás usando? Es una característica de C++ 11, por lo que es posible que el compilador no la admita o que necesite establecer algunos indicadores. p.ej. con g ++, necesitas '-std = C++ 0x'. Si no puede usar C++ 11, no se preocupe, hay otras soluciones, pero probemos primero. –

Cuestiones relacionadas