2013-10-06 3 views
7

पर जाना जाता है मेरे पास एकल-प्रकार संग्रह है जिसका प्रकार केवल रनटाइम पर जाना जाता है। एक बार टाइप परिभाषित हो जाने पर, यह कभी नहीं बदलेगा। मैं वर्तमान में एक सदिश में वस्तुओं की ओर इशारा भंडारण कर रहा हूँ, इस तरह:समरूप कंटेनर जहां टाइप केवल रनटाइम

std::vector<Animal*> v; 

मैं अगर यह सन्निहित स्मृति में उदाहरणों स्टोर करने के लिए संभव है सोच रहा हूँ। मेरा इरादा अधिक कैश-फ्रेंडली कोड लिखना और कंटेनर के माध्यम से तेज़ करना है।

मैं प्रत्येक वेक्टर के तत्व, जैसे के लिए बढ़ावा :: संस्करण इस्तेमाल कर सकते हैं,

std::vector<boost::variant< Cat, Dog > > 

लेकिन अगर sizeof(Dog) ज्यादा sizeof(Cat) से भी बड़ा तो इस मामले में जहां वस्तुओं प्रकार Cat के हैं में स्मृति की बर्बादी है।

मैं भी कंटेनर का एक संस्करण इस्तेमाल कर सकते हैं:

boost::variant< std::vector<Cat>, std::vector<Dog> > 

लेकिन मैं नहीं जानता कि कैसे iterators इस मामले में हो सकता है और अगर वे और अधिक भूमि के ऊपर का परिचय होगा।

क्या "पॉइंटर्स दृष्टिकोण का वेक्टर" सबसे अच्छा हम कर सकते हैं?

अधिक जानकारी: वस्तुओं के आकार 50 से 250 बाइट्स के बीच हैं और कंटेनर लंबाई 10 के और 1 एम तत्वों के बीच है, और मुझे कंटेनर पर दस लाख बार फिर से भरना होगा।

धन्यवाद।

संपादित: मैं एक समान प्रश्न यहाँ (अच्छा सुझावों के साथ भी) पाया: How to write cache friendly polymorphic code in C++?

+1

क्या यह कोई समझ में आता है "मेरे पास एक एकल प्रकार का संग्रह है जिसका प्रकार केवल रनटाइम पर जाना जाता है"? मेरी उम्र हो सकती है लेकिन यह –

+2

नहीं है शायद उसका अर्थ भिन्न प्रकार का संग्रह है? ऐसा लगता है कि आप विकल्प और व्यापार बंद जानते हैं। आपको बस यह तय करने की ज़रूरत है कि यह आपके लिए और अधिक महत्वपूर्ण है। – goji

+1

@EdHeal: मुझे लगता है कि इसका मतलब है कि कंटेनर में मान एकरूप प्रकार के हैं, लेकिन उस प्रकार संकलन समय पर ज्ञात नहीं है। विषम प्रकार के कंटेनर में इसे तुलना करें, जो अधिक शक्तिशाली है लेकिन इसकी आवश्यकता नहीं है। –

उत्तर

2

राइट-पूर्ण यहां पुनः लिखें, और बहुत आसान है।

मैं s3rius से सहमत हूं कि आपको अभी भी std :: vector का उपयोग करना चाहिए। आप का उपयोग आप बिल्लियों भंडारण कर रहे हैं आदर्श रूप में करता है, तो चाहता हूँ ...

std::vector<Cat> 

और यदि आप कुत्तों के भंडारण कर रहे हैं, आप चाहते हैं ...

std::vector<Dog> 

हालांकि, अगर आप रन-टाइम की जरूरत है पॉलिमॉर्फिज्म चुनने के लिए कि आप किस मामले से निपट रहे हैं।

रणनीति डिजाइन पैटर्न का एक तरीका है (या प्रेरित है)। अपने इंटरफेस के लिए उन वैक्टरों के लिए बेस क्लास को परिभाषित करें, और उस टेम्पलेट क्लास को उस इंटरफ़ेस को कार्यान्वित करें जिसमें वेक्टर शामिल है।

class Animals_IF 
{ 
    public: 
    virtual int size() const = 0; 
}; 

template<typename T> class Animals_Vector 
{ 
    private: 
    std::vector<T> store; 

    public: 
    int size() const; 
}; 

template<typename T> int Animals_Vector<T>::size() const 
{ 
    return store.size(); 
} 

यहां मुद्दा यह है कि इंटरफ़ेस Cat या Dog उल्लेख नहीं कर सकते क्योंकि यह विशेष प्रकार, निश्चित रूप से है, जिसके कारण मैं size ऊपर के उदाहरण विधि के रूप में चुना है पता नहीं है।

एक समाधान संभावित प्रकारों के boost::variant का उपयोग करके मूल्यों को पारित करना है, इसलिए प्रत्येक रणनीति/रैपर कक्षाएं यह जांच सकती हैं कि उन्हें प्राप्त होने वाले मूल्यों का उपयोग करने से पहले सही प्रकार हैं। संस्करण में रैपिंग/अनैपिंग मानों को टेम्पलेट विधियों द्वारा (गैर-टेम्पलेट) बेस क्लास में संभाला जा सकता है।

ऐसे मामलों में जहां सभी लपेटने और अनचाहे अक्षम हो जाते हैं, आपको यह निर्धारित करना होगा कि आप किस मामले से निपट रहे हैं, फिर सही रणनीति/रैपर प्रकार (आधार वर्ग नहीं) के माध्यम से कॉल करें। ऐसा करने के लिए, सभी रणनीति/रैपर मामलों का एक बढ़ावा :: संस्करण है। इससे आपको पॉइंटर-टू-बेस-क्लास भी नहीं मिलती है। वास्तव में, कक्षा में पॉइंटर-टू-बेस-क्लास और boost::variant दोनों को लपेटें (जहां आवश्यक हो वहां टेम्पलेट विधियों के साथ)।

class Animals_IF 
{ 
    public: 
    typedef boost::variant<Cat,Dog> Animal; 

    virtual int size() const = 0; 

    template<typename T> void slow_push (const T &p) 
    { 
     push_ (Animal (p)); 
    } 

    private: 
    virtual void slow_push_ (const Animal &p) = 0; 
}; 

template<typename T> class Animals_Vector 
{ 
    public: 
    int size() const; 

    void fast_push (const T &p); 

    private: 
    std::vector<T> store; 

    void slow_push_ (const Animal &p); 
}; 

template<typename T> int Animals_Vector<T>::size() const 
{ 
    return store.size(); 
} 

template<typename T> void Animals_Vector<T>::fast_push (const T &p) 
{ 
    store.push (p); 
} 

template<typename T> void Animals_Vector<T>::slow_push_ (const Animal &p) 
{ 
    const T* item = boost::get<T> (&p); 

    if (T) store.push (*item); 
    // else throw? 
} 

class Animals 
{ 
    public: 
    int size() const 
    { 
     // null check needed? 
     return ptr->size(); 
    } 

    template<typename T> void slow_push (const T &p) 
    { 
     // null check needed? 
     ptr->slow_push (p); 
    } 

    template<typename T> void fast_push (const T &p) 
    { 
     Animals_Vector<T> *lptr = boost::get<T> (&store); 
     if (lptr) lptr->fast_push (p); 
     // else throw? 
    } 

    private: 
    Animals_IF* ptr; 
    boost::variant<Animals_Vector<Cat>,Animals_Vector<Dog>> store; 
}; 

कुछ भी नहीं है कि साझा इंटरफ़ेस वास्तव में प्रदान कर सकते हैं अगर कोई (क्योंकि हर विधि मान पास करने की जरूरत है, और रैपिंग/वेरिएंट के रूप में unwrapping अस्वीकार्य है) पूरी रणनीति बात अनावश्यक है। बस अलग-अलग std :: वेक्टर प्रकारों का एक बढ़ावा :: संस्करण है।

इसके अलावा, fast_push उपरोक्त नहीं होगा क्योंकि push लाभ के लिए बहुत आसान है - विचार यह है कि दृष्टिकोण जटिल तरीकों के लिए तेज़ है जो बार-बार रन-टाइम प्रकार की जांच से बचने से बचा सकता है, ऊपर सामने।

बीटीडब्ल्यू - अच्छा सवाल।

4

मैं एक जवाब में मेरी टिप्पणी चालू करने के लिए जा रहा हूँ।

मैं कहूंगा कि आप सभी कुत्तों को vector<Dog> और सभी बिल्लियों को vector<Cat> में डालने और फिर दोनों अलग-अलग हिस्सों में फिर से स्थापित करना चाहते हैं। इस तरह आप प्रत्येक वेक्टर को अनुकूलित रूप से पैक कर सकते हैं।

कुछ सीआरटीपी के साथ आप परेशानी के बिना और अधिक जानवरों को जोड़ने में आसान बनाने के लिए इसे स्वचालित कर सकते हैं।

कुछ उदाहरण:

template <typename T> 
class Container{ 
public: 
    static std::vector<T> m_elements; //static vector will contain animals 

    //overloaded operator new adds the Animal to m_elements 
    void* operator new(size_t){ 
     m_elements.push_back(T{}); 
     return &m_elements[m_elements.size() - 1]; 
    } 
}; 

template <typename T> std::vector<T> Container<T>::m_elements; 

//some example animals 
class Dog : public Container<Dog>{ 
public: 
    std::string woof; 

    Dog(char* s = "woof"){ 
     woof = s; 
    } 
}; 

class Cat : public Container<Cat>{ 
public: 
    std::string meow; 

    Cat(char* s = "meow"){ 
     meow = s; 
    } 
}; 

int main(){ 

    new Dog("woof"); 
    new Dog("rrawoof"); 
    new Cat("meow"); 
    new Cat("meweeow"); 

    //easy iteration 
    for(auto dog : Dog::m_elements) 
     std::cout << dog.woof << "\n"; 

    for(auto cat : Cat::m_elements) 
     std::cout << cat.meow << "\n"; 

    std::cout << "end"; 
} 

यह एक अच्छा विचार ओवरलोड new या नहीं एक अलग सवाल है, लेकिन यह शो के लिए अच्छा है या नहीं।

+0

वास्तव में अच्छा जवाब। मैं इसे खुद नहीं कह सकता था। सच में नहीं। मेरे पास असली जवाब भी नहीं था। – Blade

संबंधित मुद्दे