2009-09-11 6 views
5

sizeof ऑपरेटर का उपयोग करके, मैं किसी भी प्रकार का आकार निर्धारित कर सकता हूं - लेकिन मैं रनटाइम पर पॉलिमॉर्फिक कक्षा के आकार को गतिशील रूप से कैसे निर्धारित कर सकता हूं?एक पॉलिमॉर्फिक सी ++ वर्ग

उदाहरण के लिए, मैं एक Animal के लिए सूचक है, और मैं इसे की ओर इशारा करता वास्तविक वस्तु के आकार है, जो विभिन्न अगर यह एक Cat या एक Dog है हो जाएगा प्राप्त करना चाहते हैं। क्या ऐसा करने का कोई आसान तरीका है, वर्चुअल विधि Animal::size बनाने और प्रत्येक विशिष्ट प्रकार के sizeof को वापस करने के लिए इसे ओवरलोड करना?

+3

वर्चुअल फ़ंक्शन को जोड़ने के बिना वास्तव में ऐसा करने के लिए वैसे भी नहीं है। आपको कक्षाओं के आकार को जानने की आवश्यकता क्यों है? – jmucchiello

उत्तर

6

यदि आप संभावित प्रकारों के सेट को जानते हैं, तो आप dynamic_cast कर गतिशील प्रकार का पता लगाने के लिए आरटीटीआई का उपयोग कर सकते हैं। यदि आप नहीं करते हैं, तो वर्चुअल फ़ंक्शन के माध्यम से एकमात्र तरीका है।

3

या आप टाइपिड का उपयोग कर सकते हैं, जो गतिशील_कास्ट से तेज़ हो सकता है (गतिशील_कास्ट के साथ आप पदानुक्रम में मध्यस्थ प्रकारों में भी जा सकते हैं)। प्रत्येक नए प्रकार आप creature_size कार्य करने के लिए कोड जोड़ने की आवश्यकता होगी के लिए

#include <iostream> 
#include <typeinfo> 

class Creature 
{ 
    char x[4]; 
public: 
    virtual ~Creature() {} 
}; 

class Animal: public Creature { char x[8];}; 

class Bird: public Creature { char x[16]; }; 

class Dog: public Animal { char x[32]; }; 

class Cat: public Animal { char x[64]; }; 

class Parrot: public Bird { char x[128]; }; 

unsigned creature_size(const Creature& cr) 
{ 
    if (typeid(cr) == typeid(Animal)) { 
     return sizeof (Animal); 
    } 
    else if (typeid(cr) == typeid(Dog)) { 
     return sizeof(Dog); 
    } 
    else if (typeid(cr) == typeid(Cat)) { 
     return sizeof(Cat); 
    } 
    else if (typeid(cr) == typeid(Bird)) { 
     return sizeof(Bird); 
    } 
    else if (typeid(cr) == typeid(Parrot)) { 
     return sizeof(Parrot); 
    } 
    else if (typeid(cr) == typeid(Creature)){ 
     return sizeof(Creature); 
    } 
    assert(false && "creature_size not implemented for this type"); 
    return 0; 
} 

int main() 
{ 
    std::cout << creature_size(Creature()) << '\n' 
    << creature_size(Animal()) << '\n' 
    << creature_size(Bird()) << '\n' 
    << creature_size(Dog()) << '\n' 
    << creature_size(Cat()) << '\n' 
    << creature_size(Parrot()) << '\n' ; 
} 

:

यह बल्कि बुरा लग रहा है। वर्चुअल आकार फ़ंक्शन के साथ आपको प्रत्येक कार्य में इस फ़ंक्शन को भी लागू करने की आवश्यकता होगी। हालांकि, इस समारोह में काफी आसान हो जाएगा (पूरी तरह से कॉपी-एन-pasteable, जो दिखाता है दोनों भाषा में एक सीमा है और अपने कोड डिजाइन के साथ एक समस्या हो सकती है):

virtual unsigned size() const { return sizeof(*this); } 

और आप इसे में सार कर सकते हैं बेस क्लास जिसका अर्थ यह है कि यदि आप इस विधि को ओवरराइड करना भूल जाते हैं तो यह एक कंपाइलर त्रुटि होगी।

संपादित करें: यह स्वाभाविक रूप से यह माना जाता है कि किसी भी प्राणी को आप अपना आकार जानना चाहते हैं। यदि आपके पास यह विश्वास करने का एक मजबूत कारण है कि आप कुत्ते से निपट रहे हैं - या कुत्ते का उप-वर्ग (और यदि आप उप-वर्ग हैं तो आपको परवाह नहीं है), तो स्वाभाविक रूप से आप विज्ञापन परीक्षण के लिए गतिशील_कास्ट का उपयोग कर सकते हैं।

+2

खराब दिखने से भी बदतर, हर बार जब आप एक नया जानवर बनाते हैं, तो आपको creature_size को संशोधित करना होगा। –

+1

मुझे बहुत संदेह है कि ऐसे कार्यान्वयन हैं जहां हुड के तहत 'गतिशील_कास्ट' और' टाइपिड 'नहीं है, मूल रूप से केवल उसी कोड में कॉल करें (' गतिशील_कास्ट 'के साथ कुछ चेक जो उसके आसपास मैन्युअल रूप से लपेटते हैं)। यह देखते हुए कि कुछ प्रणालियों पर आरटीटीआई (उदाहरण के लिए, जब डीएलएल शामिल होते हैं) स्ट्रिंग तुलना करने के लिए नीचे आते हैं, _if_ 'गतिशील_कास्ट' और' टाइपिड 'के बीच कोई अंतर होता है, तो वे अधिकतर उपेक्षित होंगे। – sbi

+1

@ माइकल: मैंने इसका उल्लेख किया। यदि आप गतिशील_कास्ट का उपयोग करना चाहते हैं तो आपको अपना कोड संशोधित करना होगा। यह और भी बदतर होगा: क्योंकि गतिशील_कास्ट सफलतापूर्वक इंटरमीडिएट प्रकारों (जैसे प्राणी से बर्ड तक तोते) तक जा सकता है, आपको और अधिक सावधान रहना होगा कि आप उन तुलनाओं को कैसे ऑर्डर करते हैं! और ठीक इसी कारण से गतिशील_कास्ट इसे प्राप्त कर सकता है, यह और भी खराब हो सकता है (मैंने पढ़ा है कि टाइपिड एक तुलना करता है, जबकि गतिशील_कास्ट को वास्तव में विरासत के पेड़ के माध्यम से खोजना पड़ता है।) – UncleBens

0

मैं विश्वास नहीं कर सकता किसी आविष्कार type_id (है कि) उचित लक्षण को लागू करने के बजाय ....

3

आप स्रोत वर्गों 'डिजाइन बदलने में सक्षम हैं, तो आप पूरी तरह से गतिशील बहुरूपता (जो आभासी कार्यों का उपयोग करता जगह ले सकता है) स्थिर बहुरूपता के साथ और प्रयोग CRTP idiom:

template <class TDerived> 
class Base 
{ 
public: 
    int getSize() 
    { return sizeof(TDerived); } 

    void print() 
    { 
      std::cout 
      << static_cast<TDerived*>(this)->getSize() 
      << std::endl; 
    } 

    int some_data; 
}; 

class Derived : public Base<Derived> 
{ 
public: 
    int some_other_data1; 
    int some_other_data2; 
}; 

class AnotherDerived : public Base<AnotherDerived> 
{ 
public: 
    int getSize() 
    { return some_unusual_calculations(); } 
    // Note that the static_cast above is required for this override to work, 
    // because we are not using virtual functions 
}; 

int main() 
{ 
    Derived d; 
    d.print(); 

    AnotherDerived ad; 
    ad.print(); 

    return 0; 
} 

आप के बाद से CRTP लचीलापन नहीं है जब जरूरत बहुरूपी व्यवहार कार्यक्रम केsizeof मामले की तरह संकलन समय() पर निर्धारित किया जा सकता कर सकते हैं, गतिशील बहुरूपता का रन-टाइम पर वांछित वस्तु को हल करने के लिए।

स्थिर पॉलीमोर्फिज्म को आभासी-फ़ंक्शन-कॉल ओवरहेड को हटाकर उच्च प्रदर्शन का लाभ भी है।

यदि आप बेस क्लास को टेम्पलेट करना नहीं चाहते हैं या आपको एक ही स्थान (जैसे सरणी या वेक्टर) में बेस क्लास के विभिन्न व्युत्पन्न उदाहरणों को रखने की आवश्यकता है, तो आप एक मध्यम वर्ग पर सीआरटीपी का उपयोग कर सकते हैं और पॉलीमोर्फिक को ले जा सकते हैं (विकिपीडिया में Polymorphic copy construction example के समान) है कि कक्षा में व्यवहार:

class Base 
{ 
public: 
    virtual int getSize() = 0; 

    void print() 
    { 
     std::cout << getSize() << std:endl; 
    } 

    int some_data; 
}; 

template <class TDerived> 
class BaseCRTP: public Base 
{ 
public: 
    virtual int getSize() 
    { return sizeof(TDerived); } 
}; 

class Derived : public BaseCRTP<Derived> 
{ 
    // As before ... 
}; 

class AnotherDerived : public BaseCRTP<AnotherDerived> 
{ 
    // As before ... 

    // Note that although no static_cast is used in print(), 
    // the getSize() override still works due to virtual function. 
}; 

Base* obj_list1[100]; 
obj_list1[0] = new Derived(); 
obj_list1[2] = new AnotherDerived(); 

std::vector<Base*> obj_list2; 
obj_list2.push_back(new Derived()); 
obj_list2.push_back(new AnotherDerived()); 

-
अद्यतन: मैं अब पाया एक समान है, लेकिन stackoverflow पर अधिक विस्तृत answer जो बताते हैं कि यदि हमें और अधिक व्युत्पन्न से निकाले जाते हैं उपरोक्त वर्ग (जैसे class FurtherDerived : public Derived {...}), sizeof नहीं होगा सही ढंग से रिपोर्ट करें। वह इस पर काबू पाने के लिए कोड के more complex variant देता है।