2011-01-08 26 views
9

Si tuviera que crear una clase base llamada base y las clases derivadas llamadas derived_1, derived_2 etc ... utilizo una colección de instancias de la clase base, luego cuando recuperé un elemento y Intenté usarlo. Encontraría que C++ cree que su tipo es el de la clase base, probablemente porque lo recuperé de una base de std::vector. Lo cual es un problema cuando quiero usar funciones que solo existen para la clase derivada específica, cuyo tipo sabía que era este objeto cuando lo puse en el vector.C++ clase base de fundición a la clase derivada lío

Así que lanzo el elemento al tipo que se supone que es y encontré que esto no funcionaría.

(derived_3)obj_to_be_fixed; 

Y recordé que es una cosa de puntero. Después de algunos ajustes esto ahora funcionó.

*((derived_3*)&obj_to_be_fixed); 

es esto correcto o hay por ejemplo una función abc_cast() que lo hace con menos lío?

edición:

que tenía que ampliar esto en otra pregunta, las soluciones completas se muestra allí. stackoverflow.com ... why-the-polymorphic-types-error-and-cleanup-question

+3

Espera, ¿eso es un 'std :: vector < base >' o un 'std :: vector < base * >'? Porque en el primer caso, si está almacenando objetos de la clase derivada, probablemente también haya algún corte de objeto ... –

+2

Ouch. En general, no debes hacer eso porque, como ya se dijo, tendrás slicing de objetos (http://en.wikipedia.org/wiki/Object_slicing), que en realidad reduce todos tus objetos a instancias de la clase base; si desea almacenar objetos de varias clases derivadas de la misma jerarquía de clases, debe usar un 'std :: vector < base *>'; esto mantendrá los objetos intactos, aunque igual los obtendrá como indicadores de la clase base (vea la respuesta de @Peon the Great para ver cómo lidiar con ellos). –

+0

Gracias por la información de corte de objetos, tales cosas no están claras en C++. Voy a cambiar mi mapa para mantener punteros en su lugar. – alan2here

Respuesta

18

Si almacena sus objetos en un std::vector<base>, simplemente no hay forma de volver a la clase derivada. Esto se debe a que la parte derivada se ha dividido al almacenarla en una instancia de clase base (después de todo, su vector contiene copias de sus datos, por lo que copia felizmente solo la parte base de sus objetos), haciendo que el objeto almacenado sea una verdadera instancia de clase base, en lugar de una clase derivada utilizada como clase base.

Si desea almacenar objetos polimórficos en el vector que sea un std::vector<base*> (o algún tipo de SmartPointer a la base, pero no basa en sí) y utilizar dynamic_cast<derived_3*> para lanzarlo al tipo correcto (o static_cast, si su rendimiento sensible y tiene la confianza suficiente de que está tratando de lanzar al tipo correcto (en ese caso, sucederán cosas horribles si está equivocado, así que tenga cuidado)).

1

La mayor parte del tiempo no tendrá que hacer esto. Una jerarquía de clases cuidadosamente diseñada puede manejar esto por polimorfismo (es decir, funciones virtuales).

Si realmente necesita convertir al tipo derivado, use el operador dynamic_cast.

+2

Lo que dices es correcto, pero no soluciona el problema más grave, es decir, el corte de objetos que está ocurriendo. –

+0

Supongo que los punteros se usan en vector cuando veo esta pregunta. IMO arrojar instancias de objeto directamente al vector es un mal diseño. Los asignadores de biblioteca estándar solo garantizan y se probaron únicamente con primitivas y tipos de biblioteca estándar. Cualquier cosa que no sea eso, los punteros son mejores opciones. –

+0

Bueno, el punto en la pregunta es que los punteros * no * se usan en el vector. ;) y también whu? Los contenedores de biblioteca estándar están * diseñados * para almacenar valores, no punteros. A menudo, almacenar * punteros * en un contenedor es un mal diseño. – jalf

5

Si está utilizando un vector de base, todas sus instancias son instancias base y no instancias derivadas.

Si intenta insertar una instancia derivada, el objeto será en rodajas. Insertar en un vector siempre implica una copia y el tipo de destino está determinado por el tipo de objeto que contiene el vector. Un vector no puede contener objetos de diferentes tipos.

+0

Aparentemente, según otros comentarios, debo usar std :: vector para evitar rebanar. Los vectores pueden contener una mezcla de tipos en la que, mientras sepa qué tipo de elemento es, puede obtener ese elemento y hacerlo de manera específica. Esta es una forma de hacerlo. – alan2here

+2

@ alan2here: "Los vectores pueden contener una mezcla de tipos". Esto simplemente no es verdad, me temo. Si usa 'std :: vector < base* >', tiene un vector de punteros. Los punteros pueden señalar objetos de diferentes tipos, pero los punteros son todos del mismo tipo. Y ahora, debido a que su vector es un vector de punteros, usted debe gestionar dónde se almacenan los objetos a los que apunta ese puntero, ya que los objetos ya no están almacenados en el vector. –

+0

Gracias. Lo olvidé. No sabía internamente lo que estaba sucediendo aquí. Buen punto sobre ahora tener que manejar más cosas. – alan2here

0

Lo que estás tratando de hacer no es remotamente posible. Si los objetos almacenados en su contenedor tienen el tipo base, entonces son base, punto. No son objetos derived, nunca se convertirán en objetos derived y no se pueden usar como objetos derived independientemente de lo que haga.

Su lanzamiento a través de punteros no es más que un truco que reinterpreta la memoria ocupada por el objeto base como objeto derived. Esto no tiene ningún sentido y solo puede "funcionar" por accidente.

Cuestiones relacionadas