2009-07-24 16 views
8

Ho una classe base con diverse classi che la estendono. Ho alcune utilità di libreria generiche che creano un vettore contenente puntatori alla classe base in modo che tutte le sottoclassi funzionino. Come posso trasmettere tutti gli elementi del vettore a una specifica classe figlio?Posso trasmettere std :: vector <Animal*> a std :: vector <Dog*> senza esaminare ciascun 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); 
} 

La mia soluzione semplice sarebbe simile a questa:

// 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

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

+4

Non è del tutto dupe: si noti che non sta copiando da 'vector ' a 'vector ', ma viceversa! –

+0

Immagino che stia cercando un downcast automatico/implicito! – Abhay

risposta

0

il codice come scritto metterà un po 'di puntatori nulli nel vostro cani vettore quando gli animali vettore contiene altre specializzazioni animali.

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

In genere non è consigliabile utilizzare dynamic_cast per downcasting. Probabilmente dovresti rifattorizzare il tuo codice in modo da non dover utilizzare il downcast esplicito.

Vedere CPP FAQ lite per ulteriori informazioni.

UPD Inoltre, vedere Stroustrup page (ricerca per "Perché non è possibile assegnare un vettore ad un vettore?")

10

Si potrebbe utilizzare std::transform. Si usa ancora for() internamente, ma si otterrà attuazione in due stringhe:

#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

Questo è ancora "guardando ogni elemento", il flusso è appena organizzato in modo diverso. –

0

ci sono due opzioni. Il più semplice è usare qualcosa come remove_copy_if. Non riesco a spiegare il motivo per cui lo chiamano, ma copia gli elementi da un contenitore all'altro che non soddisfano il predicato. Ecco l'idea di base (non testata):

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 

}

Suppongo che un modo di guardare remove_copy_if è quello di pensare ad esso come copy_unless.

Un approccio alternativo, se si basa il codice solo sugli iteratori, è quello di avvolgere l'iteratore per il vettore < Animale *> con uno che restituisce solo i cani dalla raccolta. Il vantaggio chiave qui è che hai ancora un solo contenitore, ma ovviamente paghi un po 'di più dato che l'algoritmo navigherà su tutta la collezione di animali.

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; 
    } 
    // ... 
}; 

Questo è molto approssimativo, ma spero che mostri il principio di base.

0

Se stai dicendo che è possibile garantire che ogni elemento è in realtà un cane poi basta static_cast cioè

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])); 
    } 
} 

Io di solito sempre ottenere una reazione istintiva da parte di persone che questo è male e si dovrebbe sempre usare dynamic_cast ma, in realtà, se puoi fare delle garanzie sul tipo, allora è perfettamente sicuro e IMO una cosa sensata da fare.

Inoltre, la garanzia implicherebbe che il nuovo vettore abbia le stesse dimensioni, quindi riservare lo stesso spazio per evitare di effettuare assegnazioni in ogni push_back.In un ciclo alternativa ho usato un indice solo perché penso sempre iterare utilizzando un indice deve essere più veloce di un iteratore, ma che probabilmente è una sciocchezza :)

0

Quando si può assicurare, che il vostro std::vector<Animal*> contiene solo Dog* è possibile utilizzare reinterpret_cast .

0

Mescolando la metodologia std::transform con un static_cast (perché si è certi della sua sicurezza) potrebbe essere simile:

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