2009-07-24 16 views
8

मेरे पास एक कक्षा है जिसमें कई वर्ग इसे विस्तारित करते हैं। मेरे पास कुछ जेनेरिक लाइब्रेरी यूटिलिटीज हैं जो बेस क्लास में वेक्टर युक्त पॉइंटर्स बनाती हैं ताकि कोई भी उप-वर्ग काम करेगा। मैं वेक्टर के सभी तत्वों को एक विशिष्ट बाल वर्ग में कैसे डाल सकता हूं?क्या मैं प्रत्येक तत्व को देखे बिना std :: vector <Animal*> std :: vector <Dog*> पर जा सकता हूं?

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

मेरे अनुभवहीन समाधान कुछ इस तरह दिखेगा:

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

डुप्लिकेट: http://stackoverflow.com/questions/902667/stl-container-assignment- और-कॉन्स्ट-पॉइंटर्स – GManNickG

+4

यह काफी डुप्ली नहीं है - ध्यान दें कि वह 'वेक्टर ' से 'वेक्टर ' पर प्रतिलिपि नहीं बना रहा है, लेकिन दूसरी तरफ! –

+0

मुझे लगता है कि वह एक स्वचालित/निहित डाउनकास्ट की तलाश में है! – Abhay

उत्तर

0

आपका कोड के रूप में लिखा अपने कुत्तों वेक्टर में अशक्त संकेत के एक झुंड रखा जाएगा जब जानवरों वेक्टर अन्य जानवर विशेषज्ञताओं में शामिल है।

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

आमतौर पर डाउनकास्टिंग के लिए गतिशील_कास्ट का उपयोग करना बहुत अच्छा नहीं है। आपको शायद अपने कोड को दोबारा सुधारना चाहिए ताकि आपको स्पष्ट डाउनकास्टिंग का उपयोग करने की आवश्यकता न हो।

कुछ और जानकारी के लिए CPP FAQ lite देखें।

युपीडी इसके अलावा,

10

आप std::transform इस्तेमाल कर सकते हैं ("मैं एक वेक्टर के लिए एक वेक्टर असाइन नहीं कर सकते क्यों?" के लिए खोज) Stroustrup page देखते हैं। यह अभी भी for() आंतरिक रूप से उपयोग करता है, लेकिन आप दो स्ट्रिंग कार्यान्वयन मिलेगा:

#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

यह अभी भी "प्रत्येक तत्व को देख रहा है", प्रवाह सिर्फ अलग-अलग व्यवस्थित है। –

0

पास दो विकल्प हैं। remove_copy_if जैसे कुछ का उपयोग करना सबसे आसान है। मैं यह समझा नहीं सकता कि वे इसे क्यों कहते हैं, लेकिन यह तत्वों को एक कंटेनर से दूसरे में कॉपी करता है जो भविष्यवाणी को पूरा नहीं करता है। यहाँ मूल विचार (untested) है:

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 

}

मुझे लगता है एक तरह से remove_copy_if को देखने के लिए copy_unless के रूप में यह सोचने के लिए है।

एक वैकल्पिक दृष्टिकोण, यदि आप केवल अपने कोडरेटर के आसपास अपना कोड आधार रखते हैं, तो वेक्टर < पशु *> के लिए इटरेटर को लपेटना है जो केवल संग्रह से कुत्तों को लौटाता है। यहां का मुख्य लाभ यह है कि आपके पास अभी भी केवल एक कंटेनर है, लेकिन निश्चित रूप से आप थोड़ी अधिक भुगतान करते हैं क्योंकि आप एल्गोरिदम जानवरों के पूरे संग्रह पर नेविगेट करेंगे।

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

यह बहुत मोटा है, लेकिन मुझे आशा है कि यह आपको मूल सिद्धांत दिखाएगा।

0

आप कह रहे हैं, तो आप गारंटी ले सकते हैं कि कि प्रत्येक तत्व वास्तव में एक कुत्ता है तो बस static_cast यानी

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

मैं आमतौर पर हमेशा लोगों से एक घुटने झटका प्रतिक्रिया है कि यह बुरा है मिलता है और आप हमेशा का उपयोग करना चाहिए dynamic_cast लेकिन, हकीकत में, यदि आप इस प्रकार के बारे में गारंटी दे सकते हैं तो यह पूरी तरह से सुरक्षित है और आईएमओ करने के लिए एक समझदार बात है।

इसके अलावा, आपकी गारंटी का अर्थ यह होगा कि नए वेक्टर का आकार समान है, इसलिए प्रत्येक push_back में किसी भी आवंटन से बचने के लिए एक ही स्थान आरक्षित करें।एक विकल्प के पाश के रूप में मैं एक सूचकांक का उपयोग किया है सिर्फ इसलिए कि मैं हमेशा एक सूचकांक का उपयोग कर एक इटरेटर से जल्दी होना चाहिए पुनरावृत्ति लगता है लेकिन यह है कि शायद बकवास है :)

0

जब आपको आश्वस्त कर सकता है, जो आपके std::vector<Animal*> केवल शामिल Dog* आप reinterpret_cast उपयोग कर सकते हैं ।

0

एक static_cast साथ std::transform कार्यप्रणाली (क्योंकि आप अपनी सुरक्षा के कुछ कर रहे हैं) मिश्रण दिखाई देगा:

std::transform(animals.begin(), animals.end(), 
       std::back_insert_iterator<std::vector<Dog*>>(dogs), 
       [](auto ptr) { return static_cast<Dog*>(ptr); }); 
संबंधित मुद्दे