2009-07-24 17 views
8

Tengo una clase base con varias clases que la amplían. Tengo algunas utilidades de biblioteca genéricas que crean un vector que contiene punteros a la clase base para que cualquiera de las subclases funcione. ¿Cómo puedo convertir todos los elementos del vector a una clase infantil específica?¿Puedo lanzar std :: vector <Animal*> a std :: vector <Dog*> sin mirar cada elemento?

// A method is called that assumes that a vector containing 
// Dogs casted to Animal is passed. 
void myDogCallback(vector<Animal*> &animals) { 
    // I want to cast all of the elements of animals to 
    // be dogs. 
    vector<Dog*> dogs = castAsDogs(animals); 
} 

Mi solución ingenua sería algo como esto:

// A method is called that assumes that a vector containing 
// Dogs casted to Animal is passed. 
void myDogCallback(vector<Animal*> &animals) { 
    // I want to cast all of the elements of animals to 
    // be dogs. 
    vector<Dog*> dogs; 
    vector<Animal*>::iterator iter; 
    for (iter = animals.begin(); iter != animals.end(); ++iter) { 
     dogs.push_back(dynamic_cast<Dog*>(*iter)); 
    } 
} 
+1

duplicado: http://stackoverflow.com/questions/902667/stl-container-assignment- and-const-pointers – GManNickG

+4

No es del todo engañoso, tenga en cuenta que no está copiando del 'vector ' al' vector ', sino al revés. –

+0

¡Supongo que está buscando un downcast automático/implícito! – Abhay

Respuesta

0

Su código como está escrito va a poner un montón de punteros nulos en el vector de perros cuando el vector de animales contiene otras especializaciones animales.

vector<Dog*> dogs; 
vector<Animal*>::iterator iter; 
Dog* dog; 

for(iter = animals.begin(); iter != animals.end(); ++iter) 
{ 
    dog = dynamic_cast<Dog*>(*iter); 
    if(dog) 
    { 
    dogs.push_back(dog); 
    } 
} 
0

Por lo general, no es muy bueno utilizar dynamic_cast para downcasting. Probablemente deberías refactorizar tu código para que no necesites usar downcasting explícito.

Consulte CPP FAQ lite para obtener más información.

UPD Además, vea Stroustrup page (búsqueda de "¿Por qué no puedo asignar un vector de un vector?")

10

Usted podría utilizar std::transform. Se sigue utilizando for() internamente, pero obtendrá la ejecución de dos cuerdas:

#include <vector> 
#include <algorithm> 
using namespace std; 

struct Animal { virtual ~Animal() {} }; 
struct Dog : Animal { virtual ~Dog() {} }; 

template<typename Target> 
struct Animal2Target { Target* operator()(Animal* value) const { return dynamic_cast<Target*>(value); } }; 

void myDogCallback(vector<Animal*> &animals) { 
{ 
    vector<Dog*> dogs; 
    transform(animals.begin(), animals.end(), dogs.begin(), Animal2Target<Dog>()); 
}
+2

Esto sigue "mirando cada elemento", el flujo se organiza de forma diferente. –

0

hay dos opciones. Lo más simple es usar algo como remove_copy_if. No puedo explicar por qué lo llaman así, pero copia los elementos de un contenedor a otro que no cumplen el predicado. Aquí está la idea básica (no probado):

struct IsDog : unary_function < Animal *, bool > { 
    bool operator()(Animal * animal) const { 
    return dynamic_cast <Dog*> (animal); 
    } 
}; 

void foo (vector<Animal*> animals) { 
    vector<Dog*> dogs; 
    std::remove_copy_if (animals.begin() 
    , animals.end() 
    , back_inserter (dogs) 
    , std::not1 (IsDog())); // not1 here negates the result of IsDog! 


    // dogs now contains only animals that were dogs 

}

supongo que una manera de mirar remove_copy_if es pensar en él como copy_unless.

Un enfoque alternativo, si basa su código solo alrededor de los iteradores, es ajustar el iterador para el vector < Animal *> con uno que solo devuelve los perros de la colección. La principal ventaja aquí es que solo tienes un contenedor, pero por supuesto pagas un poco más ya que tu algoritmo navegará por toda la colección de animales.

class dog_iterator // derive from std::iterator probably with bidirectinoal tag 
{ 
private: 
    vector<Animals*>::iterator getNextDogIter (vector<Animals*>::iterator iter) { 
    while (iter != m_end) { 
     if (0 != dynamic_cast<Dog*> (*iter)) { 
     break; 
     } 
     ++iter; 
    } 
    return iter; 
    } 

public: 
    dog_iterator (vector<Animals*>::iterator iter, vector<Animals*>::iterator end) 
    : m_end (end) 
    , m_iter (getNextDogIter (iter)) 
    { 
    } 

    // ... all of the usual iterator functions 

    dog_iterator & operator++() 
    { 
    // check if m_iter already is at end - otherwise: 
    m_iter = getNextDogIter (m_iter + 1); 
    return *this; 
    } 
    // ... 
}; 

Esto es muy duro, pero espero que te muestre el principio básico.

0

Si usted está diciendo que usted puede garantizar que cada elemento es realmente un perro a continuación, sólo static_cast es decir

void myDogCallback(vector<Animal*> &animals) { 

    const vector<Animal*>::size_type numAnimals = animals.size(); 

    vector<Dog*> dogs; 
    dogs.reserve(numAnimals); 

    for (vector<Animal*>::size_type i = 0; i < numAnimals; ++i) { 
     dogs.push_back(static_cast<Dog*>(animals[i])); 
    } 
} 

por lo general siempre me dan una reacción instintiva de la gente que esto es malo y usted debe utilizar siempre dynamic_cast pero, en realidad, si puede hacer garantías sobre el tipo, entonces es perfectamente seguro y la OMI es una medida sensata.

Además, su garantía implicaría que el nuevo vector tiene el mismo tamaño, así que reserve el mismo espacio para evitar hacer asignaciones en cada push_back.Como un bucle alternativo que he usado un índice sólo porque siempre pienso iteración a través de un índice debe ser más rápido que un iterador, pero eso es probablemente sin sentido :)

0

Cuando se puede asegurar, que su std::vector<Animal*> incluye solamente Dog* puede utilizar reinterpret_cast .

0

Mezcla la metodología std::transform con un static_cast (porque está seguro de su seguridad) puede verse como:

std::transform(animals.begin(), animals.end(), 
       std::back_insert_iterator<std::vector<Dog*>>(dogs), 
       [](auto ptr) { return static_cast<Dog*>(ptr); }); 
Cuestiones relacionadas