2011-03-28 15 views
5

¿Está bien lanzar un contenedor STL del tipo Base al tipo Derivado? Por ejemplo, tengo dos vectores. El primero es en tipo de una clase Base, el segundo es en tipo de una clase Derive.¿Está bien lanzar un contenedor STL con tipo Base al tipo Derivado?

class Base 
{ 
// Code 
}; 

class Derive : public Base 
{ 
// Code 
}; 

Uso

vector<Base*>* vec_base = new vector<Base*>; 

    // Add some Derive type data to vec_base 

    vector<Derive*>* vec_derive = (vector<Derive*>*)(vec_base); 

    // Using elements as Derive pointers. Works fine. 

¿Está bien? (Funciona bien, pero quería obtener algunos comentarios sobre esto). Muchas gracias.

EDITAR: Actualizando según las respuestas.

Digamos, si uso ese vector con cuidado, y no lo usaré con herencia múltiple y no insertaré objetos que no sean Derive, ¿está bien? (Supongo que no lo es)

Y muchas gracias por las respuestas.

+0

Re: actualización: nada que puedas hacer sin usar algo como std :: transform hará que use cualquier elenco que no sea reinterpret_cast, que no es lo que estás buscando – Flexo

+0

@awoodland Entonces, reinterpretar_cast es malo incluso si no tipos polimórficos? Quiero decir, incluso si estoy 100% seguro de que "sé el tipo exacto" del vector reinterpret_cast no está definido para este caso. – Morpheus

+1

reinterpret_cast es definitivamente malo en std :: vector, el problema es que no hay razón (portable, estandarizada) para suponer que el diseño de la memoria para std :: vector y std :: vector sean compatibles para que esto funcione. – Flexo

Respuesta

8

Esto definitivamente no está bien, y es uno de los ejemplos de errores de enmascaramiento de moldes de estilo c. "Funciona para mí" no es indicativo de un comportamiento bien definido en este caso.

Si realmente quieres hacer esto me gustaría sugerir:

#include <vector> 
#include <algorithm> 
#include <iterator> 

using namespace std; 

class Base 
{ 
// Code 
virtual ~Base(); 
}; 

class Derrive : public Base 
{ 
// Code 
}; 

Derrive *convert(Base * in) { 
    // assert here? 
    return dynamic_cast<Derrive*>(in); 
} 

int main() { 
    vector<Base*>* vec_base = new vector<Base*>; 

    // Add some Derrive type data to vec_base 

    vector<Derrive*>* vec_derrive = new vector<Derrive*>; 

    transform(vec_base->begin(), vec_base->end(), back_insert_iterator<vector<Derrive*> >(*vec_derrive), convert); 
} 
+0

Esa es una buena forma de hacerlo. Muchas gracias – Morpheus

+2

Es una pena que dynamic_cast no sea una función porque entonces posiblemente no necesites la función de conversión para envolver dynamic_cast en absoluto. – Flexo

+0

Cuando usa el vec_derrive, compruebe que cada miembro tenga nulo antes de usarlo. Si alguno de los dynamic_casts falla, obtendrá entradas nulas en su vector. – Tim

0

No. Eso no va a funcionar bien.

Di que tengo Derrive2 que se deriva de Base. Puedo poner eso en el contenedor STL, pero no se lanzará de forma segura a Derrive.

+0

Ok, digo que usaré ese vector con cuidado y no pondré el tipo Derrived2 en vector. Ahora, ¿está bien? – Morpheus

+0

Sigue siendo una mala idea. –

+0

@Morpheus No, no está bien, los tipos de vectores aún no están relacionados. –

1

Esto no está bien. tipos de plantilla con diferentes T s son tipos no relacionados (a pesar de que ambos dicen std::vector, y utilizando una conversión de estilo C justo le permite alejarse con un comportamiento indefinido.

Si parece que funciona por ahora, considérese la mala suerte de que se no se bloqueó.

Si sabe que TODOS los elementos en el vector son de la clase derivada, simplemente haga que el vector señale los objetos derivados por adelantado. Si no lo sabe, entonces el molde no es seguro

2

Estás haciendo un elenco de estilo c, que esencialmente está haciendo un reinterpret_cast, que le dice al compilador "trata x como y a partir de ahora, y solo confía en mí que es wo rks ". Entonces definitivamente compilará, pero es una mala idea. Aquí no hay ningún tipo de seguridad, y puede funcionar algunas veces, pero se bloqueará imprevisiblemente en otros momentos.

Lo que puede hacer en su lugar:

for (unsigned int i=0; i < vec_base->length(); i++) 
{ 
    Derrive* d = dynamic_cast<Derrive*> (vec_base[i]); 
    if (d) { 
    // this element is a Derrive instance, so we can treat it like one here 
    } 
    // else, skip it, log an error, throw an exception, whatever, 
    // this element in the vector is not of type Derrive 
} 
+0

Sí, debería ser el camino. Pero no puedo permitir que los usuarios usen dynamic_cast, ya que está en la ruta crítica. Pensé que al forzar al compilador a convertir al tipo Derrive, debería funcionar – Morpheus

0

La manera más segura de hacer esto es utilizando std :: transformar.

Pero, dado que dentro de la implementación de std :: list, T = Base * o T = Derivado * se comporta de la misma manera (ambos tienen el mismo tamaño), una lista tiene la misma estructura interna que una lista. Por lo que es posible hacer el siguiente truco:

vector<Derived*> vector2 = *(reinterpret_cast< vector<Derived*>* >(&vector1)); 

Nota: Mi respuesta es informativo, por favor, seguir con el std :: transformar el enfoque. No estoy seguro si en la implementación de STDC++ otros que no sean GCC, el vector se comportará de la misma manera, es decir.No estoy seguro si la afirmación "list < Base *> y list < Derivated *> tienen la misma estructura interna" es cierto en otras implementaciones.

Cuestiones relacionadas