¿Cuál sería la forma más segura para almacenar objetos de clases derivadas de una interfaz común en un recipiente común?

StackOverflow https://stackoverflow.com/questions/2489533

Pregunta

Me gustaría manejar un montón de objetos de clases derivadas de una clase de interfaz compartida en un recipiente común.

Para ilustrar el problema, digamos que estoy construyendo un juego que contendrá diferentes actores. Vamos a llamar a la interfaz y IActor derivan 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. En este ejemplo, obviamente, no va a funcionar, pero que es más o menos la razón por la que estoy pidiendo aquí.

Me gustaría saber: ¿Cuál sería la mejor, manera, más segura de más alto nivel para gestionar esos objetos en un contenedor heterogénea común? Yo sé de una variedad de enfoques (boost :: Cualquier, void *, clase de controlador de impulso :: shared_ptr, Boost.Pointer de contenedores, moldeado dinámico), pero no puedo decidir cual sería el camino a seguir aquí.

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

Ayuda muy apreciada:.)

¿Fue útil?

Solución

Como se habrá adivinado que necesita para almacenar los objetos como punteros.
Yo prefiero usar los contenedores puntero del impulso (en lugar de un contenedor normal de punteros inteligentes).

La razón de esto es el acceso al contenedor ptr impulso los objetos como si fueran objetos (volviendo referencias) en lugar de punteros. Esto hace que sea más fácil de usar funtores y algoritmos estándar en los envases.

La desventaja de punteros inteligentes es que está compartiendo la propiedad.
Esto no es lo que realmente quiere. ¿Quieres la propiedad de estar 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));

Otros consejos

Para resolver el problema que usted ha mencionado, a pesar de que va en la dirección correcta, pero que está haciendo de la manera equivocada. 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 derivados Enemy clases y Civilian en su caso.
  • Usted tiene que elegir un recipiente adecuado con almacenará el objeto. Usted ha tomado un std::vector<IActor> que no es una buena opción porque
    • En primer lugar, cuando va a agregar objetos al vector que está dando lugar a objeciones de corte. Esto significa que sólo la parte IActor de Enemy o Civilian se está almacenando en lugar de todo el objeto.
    • En segundo lugar es necesario llamar a funciones dependiendo del tipo de objeto (virtual functions), el cual sólo puede ocurrir 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*>. Sin embargo, una mejor opción sería utilizar container of smart pointers lo que le ahorrará 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();
     }
}

Lo que es más o menos similar a su código original a excepción de la parte inteligente punteros

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

Si desea que el contenedor de poseer exclusivamente los elementos que lo integran, utilice un recipiente puntero Boost: están diseñados para ese trabajo. De lo contrario, utilizar un recipiente de shared_ptr<IActor> (y por supuesto utilizarlos de manera adecuada, lo que significa que todos los que necesitan compartir la propiedad utiliza shared_ptr).

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

void* requiere que usted haga la gestión de memoria manual, por lo que está fuera. Boost.Any es una exageración cuando los tipos están relacionados por herencia -. Polimorfismo estándar hace el trabajo

Ya sea que necesite dynamic_cast o no es una cuestión ortogonal - si los usuarios del contenedor sólo necesitan la interfaz IActor, y que o bien (a) realiza todas las funciones de la interfaz virtual, o bien (b) utilizar la no interfaz de lenguaje virtual, entonces no es necesario dynamic_cast. Si los usuarios del contenedor saben que algunos de los objetos IActor son "realmente" los civiles, y quiere hacer uso de las cosas que están en la interfaz civil pero no IActor, entonces usted va a necesitar moldes (o un rediseño).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top