2010-03-22 9 views
7

Me gustaría administrar un conjunto de objetos de clases derivados de una clase de interfaz compartida en un contenedor común.¿Cuál sería la forma más segura de almacenar objetos de clases derivados de una interfaz común en un contenedor común?

Para ilustrar el problema, digamos que estoy construyendo un juego que contendrá diferentes actores. Llamemos a la interfaz IActor y obtenga Enemy y Civilian de ella.

Ahora, la idea es tener mi juego bucle principal sea capaz de hacer esto:

// somewhere during init 
std::vector<IActor> ActorList; 
Enemy EvilGuy; 
Civilian CoolGuy; 
ActorList.push_back(EvilGuy); 
ActorList.push_back(CoolGuy); 

y

// main loop 
while(!done) { 
    BOOST_FOREACH(IActor CurrentActor, ActorList) { 
     CurrentActor.Update(); 
     CurrentActor.Draw(); 
    } 
} 

... o algo por el estilo. Este ejemplo, obviamente, no funcionará, pero esa es más o menos la razón por la que estoy preguntando aquí.

Me gustaría saber: ¿Cuál sería la forma mejor, más segura y de más alto nivel para administrar esos objetos en un contenedor heterogéneo común? Conozco una variedad de enfoques (Boost :: Any, void *, clase de controlador con boost :: shared_ptr, Boost.Pointer Container, dynamic_cast) pero no puedo decidir cuál sería el camino a seguir aquí.

También me gustaría hacer hincapié en que quiero alejarme lo más posible de la administración manual de la memoria o los punteros anidados.

Ayuda muy apreciada :).

Respuesta

3

Como ya habrá adivinado, debe almacenar los objetos como punteros.
Prefiero usar los contenedores del puntero de refuerzo (en lugar de un contenedor normal de punteros inteligentes).

La razón de esto es que el contenedor ptr boost accede a los objetos como si fueran objetos (referencias de retorno) en lugar de punteros. Esto facilita el uso de funtores y algoritmos estándar en los contenedores.

La desventaja de los punteros inteligentes es que usted comparte la propiedad.
Esto no es lo que realmente quieres. Desea que la propiedad esté en un solo lugar (en este caso, el contenedor).

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian()); 

y

std::for_each(ActorList.begin(), 
       ActorList.end(), 
       std::mem_fun_ref(&IActor::updateDraw)); 
+0

le gustó la forma en que usa for_each –

+0

¿Puede explicar su for_each y cómo la usaría con BOOST_FOREACH? – Svenstaro

+0

std :: for_each (I1, I2, Acción).Aplica la Acción a todos los valores (en este caso llama al método updateDraw) en el rango entre I1 e I2 (sin incluir I2). Donde I1, I2 son iteradores. Ver: http://www.sgi.com/tech/stl/for_each.html –

4

Mi reacción instantánea es que debe almacenar punteros inteligentes en el contenedor, y asegúrese de que la clase base define suficientes métodos virtuales (puros) que nunca necesita para dynamic_cast volver a la clase derivada.

10

Para resolver el problema que ha mencionado, aunque vaya en la dirección correcta, pero lo está haciendo de la manera incorrecta. Esto es lo que tendría que hacer

  • definir una clase base (que ya lo están haciendo) con funciones virtuales que pueden sustituir por clases derivadas Enemy y Civilian en su caso.
  • Tienes que elegir un contenedor adecuado con el que almacenarás tu objeto. Ha tomado un std::vector<IActor> que no es una buena opción porque
    • En primer lugar, cuando agrega objetos al vector, está provocando el corte de objetos. Esto significa que solo se almacena la parte IActor de Enemy o Civilian en lugar de todo el objeto.
    • En segundo lugar, debe llamar a las funciones según el tipo de objeto (virtual functions), lo que solo puede suceder si utiliza punteros.

Tanto de la razón por encima del punto al hecho de que es necesario utilizar un recipiente que puede contener punteros, algo así como std::vector<IActor*>. Pero una mejor opción sería usar container of smart pointers que lo salvará de los dolores de cabeza de administración de memoria.Puede utilizar cualquiera de los punteros inteligentes dependiendo de su necesidad (pero no auto_ptr)

Esto es lo que el código se vería así

// somewhere during init 
std::vector<some_smart_ptr<IActor> > ActorList; 
ActorList.push_back(some_smart_ptr(new Enemy())); 
ActorList.push_back(some_smart_ptr(new Civilian())); 

y

// main loop 
while(!done) 
{ 
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    { 
     CurrentActor->Update(); 
     CurrentActor->Draw(); 
    } 
} 

¿Qué es más o menos similar a la su código original excepto los punteros inteligentes parte

+3

específicamente que quieren un puntero inteligente con la semántica de copia –

+0

yup i de acuerdo con eso –

+1

me gusta su enfoque, ya que se ve muy limpia para un enfoque basado en los punteros. Sin embargo, como han señalado otros, lo que quiero hacer es probablemente mejor utilizando Boost Pointer Containers ya que parecen estar hechos para lo que quiero lograr. Probaré tu enfoque si eso falla. ¿O ve algún motivo * no * para usar Boost Pointer Containers? – Svenstaro

3

Si desea que el contenedor sea el propietario exclusivo de los elementos, utilice un contenedor de puntero Boost: son diseñado para ese trabajo. De lo contrario, use un contenedor de shared_ptr<IActor> (y, por supuesto, úselos correctamente, lo que significa que todos los que necesitan compartir la propiedad usan shared_ptr).

En ambos casos, asegúrese de que el destructor de IActor sea virtual.

void* requiere que hagas la gestión de la memoria manual, así que está fuera. Boost.Any es excesivo cuando los tipos están relacionados por herencia: el polimorfismo estándar hace el trabajo.

Si necesita dynamic_cast o no, es un problema ortogonal: si los usuarios del contenedor solo necesitan la interfaz IActor, y usted (a) hace que todas las funciones de la interfaz sean virtuales, o bien (b) usa el no -Idioma de interfaz virtual, entonces no necesitas dynamic_cast. Si los usuarios del contenedor saben que algunos de los objetos de IActor son "realmente" civiles, y desean utilizar cosas que están en la interfaz civil pero no en IActor, entonces necesitarán moldes (o un rediseño).

Cuestiones relacionadas