2009-07-24 7 views
8

Ich habe eine Basisklasse mit mehreren Klassen, die es erweitern. Ich habe einige generische Bibliotheksprogramme, die einen Vektor erstellen, der Zeiger auf die Basisklasse enthält, so dass jede der Unterklassen funktioniert. Wie kann ich alle Elemente des Vektors in eine bestimmte Kindklasse umwandeln?Kann ich std :: vector <Animal*> nach std :: vector <Dog*> werfen, ohne jedes Element zu betrachten?

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

Meine naive Lösung würde wie folgt aussehen:

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

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

+4

Es ist nicht ganz dip - beachte, dass er nicht von "vector " zu "vector " kopiert, aber umgekehrt! –

+0

Ich denke er sucht nach einem automatischen/impliziten Downcast! – Abhay

Antwort

0

als Ihr Code wird eine Reihe von Null-Zeiger in Ihre Hunde Vektor setzen geschrieben, wenn die Tiere Vektor andere Tier Spezialisierungen enthält.

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

Normalerweise ist es nicht sehr gut für Downcasting zu verwenden dynamic_cast. Sie sollten Ihren Code wahrscheinlich umgestalten, damit Sie keine expliziten Downcasts verwenden müssen.

Weitere Informationen finden Sie unter CPP FAQ lite.

UPD Auch sehen Stroustrup page (Suche nach „Warum kann ich nicht um einen Vektor zu einem Vektor? Zuweisen“)

10

Sie std::transform nutzen könnten. Es nutzt noch for() intern, aber Sie werden mit zwei String-Implementierung erhalten:

#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

Dies ist immer noch "Blick auf jedes Element", der Fluss ist nur anders angeordnet. –

0

Es gibt zwei Möglichkeiten. Am einfachsten ist es, etwas wie remove_copy_if zu verwenden. Ich kann nicht erklären, warum sie es so nennen, aber es kopiert die Elemente von einem Behälter in einen anderen, die nicht das Prädikat erfüllen. Hier ist die Grundidee (ungetestet):

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 

}

Ich nehme an, ein Weg, um remove_copy_if zu suchen ist es als copy_unless zu denken. Ein alternativer Ansatz, wenn Sie Ihren Code nur auf Iteratoren aufbauen, besteht darin, den Iterator für Vektor < Animal *> mit einem zu umbrechen, der nur die Hunde aus der Sammlung zurückgibt. Der Hauptvorteil ist, dass Sie immer noch nur einen Container haben, aber natürlich zahlen Sie ein bisschen mehr, da Ihr Algorithmus über die gesamte Sammlung von Tieren navigieren wird.

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

Das ist sehr grob, aber ich hoffe, es zeigt Ihnen das Grundprinzip.

0

Wenn Sie sagen, dass Sie, dass jedes Element ein Hund ist garantieren können wirklich dann static_cast nur dh

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

ich in der Regel immer eine reflexartige Reaktion von Menschen, dass diese schlecht ist und Sie immer verwenden sollen, dynamic_cast aber in Wirklichkeit, wenn Sie Garantien über den Typ dann können, ist es vollkommen sicher und IMO eine vernünftige Sache zu tun.

Außerdem würde Ihre Garantie bedeuten, dass der neue Vektor die gleiche Größe hat, also reservieren Sie den gleichen Platz, um zu vermeiden, dass Sie in jedem push_back Allokationen vornehmen.Als Alternative Schleife habe ich einen Index nur verwendet, weil ich als ein Iterator schneller immer denken Iterieren einen Index verwenden, müssen aber das ist wahrscheinlich Unsinn :)

0

Wenn Sie versichern kann, dass Ihr std::vector<Animal*> enthält nur Dog* Sie reinterpret_cast verwenden können .

0

Mischen die std::transform Methodik mit einem static_cast (weil Sie einige ihrer Sicherheit sind) aussehen könnte:

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