RTTI का उपयोग करने पर 'शुद्ध बहुरूपता' क्यों बेहतर है?


106

लगभग हर सी ++ संसाधन मैंने देखा है कि इस तरह की चर्चा करने से मुझे पता चलता है कि मुझे आरटीटीआई (रन-टाइम प्रकार की पहचान) का उपयोग करने के लिए बहुरूपी दृष्टिकोण पसंद करना चाहिए। सामान्य तौर पर, मैं इस तरह की सलाह को गंभीरता से लेता हूं, और औचित्य को समझने और समझने की कोशिश करूंगा - आखिरकार, C ++ एक शक्तिशाली जानवर है और इसकी पूरी गहराई को समझना कठिन है। हालाँकि, इस विशेष प्रश्न के लिए, मैं एक रिक्त चित्र बना रहा हूं और यह देखना चाहूंगा कि इंटरनेट किस तरह की सलाह दे सकता है। सबसे पहले, मुझे संक्षेप में बताएं कि मैंने अब तक क्या सीखा है, सामान्य कारणों को सूचीबद्ध करके कि आरटीटीआई को "हानिकारक क्यों माना जाता है":

कुछ संकलक इसका उपयोग नहीं करते / RTTI हमेशा सक्षम नहीं होता है

मैं वास्तव में इस तर्क को नहीं खरीदता। यह कहने की तरह है कि मुझे C ++ 14 सुविधाओं का उपयोग नहीं करना चाहिए, क्योंकि वहाँ बाहर संकलक हैं जो इसका समर्थन नहीं करते हैं। और फिर भी, कोई भी मुझे C ++ 14 सुविधाओं का उपयोग करने से हतोत्साहित नहीं करेगा। अधिकांश प्रोजेक्ट्स का उपयोग करने वाले कंपाइलर पर प्रभाव पड़ेगा, और यह कैसे कॉन्फ़िगर किया गया है। यहां तक ​​कि जीसीसी मैनपेज को उद्धृत करते हुए:

-fno-rtti

C ++ रन-टाइम प्रकार पहचान सुविधाओं (डायनेमिक_कास्ट और टाइपिड) द्वारा उपयोग के लिए वर्चुअल फ़ंक्शंस के साथ हर वर्ग के बारे में जानकारी के अक्षम पीढ़ी। यदि आप भाषा के उन हिस्सों का उपयोग नहीं करते हैं, तो आप इस ध्वज का उपयोग करके कुछ स्थान बचा सकते हैं। ध्यान दें कि अपवाद हैंडलिंग समान जानकारी का उपयोग करता है, लेकिन G ++ इसे आवश्यकतानुसार उत्पन्न करता है। डायनामिक_कास्ट ऑपरेटर का उपयोग अभी भी उन जातियों के लिए किया जा सकता है जिन्हें रन-टाइम प्रकार की जानकारी की आवश्यकता नहीं है, अर्थात "शून्य *" या असंदिग्ध आधार वर्गों के लिए जाती हैं।

यह मुझे बताता है कि अगर मैं आरटीटीआई का उपयोग नहीं कर रहा हूं, तो मैं इसे अक्षम कर सकता हूं। यदि आप बूस्ट का उपयोग नहीं कर रहे हैं तो यह कहना पसंद है, आपको इसे लिंक करने की आवश्यकता नहीं है। मुझे उस मामले के लिए योजना बनाने की ज़रूरत नहीं है जहां कोई व्यक्ति संकलन कर रहा है -fno-rtti। साथ ही, कंपाइलर इस मामले में जोर से और स्पष्ट रूप से विफल हो जाएगा।

इसमें अतिरिक्त मेमोरी खर्च होती है / यह धीमी हो सकती है

जब भी मुझे आरटीटीआई का उपयोग करने के लिए लुभाया जाता है, तो इसका मतलब है कि मुझे अपनी कक्षा के कुछ प्रकार की जानकारी या लक्षणों का उपयोग करने की आवश्यकता है। यदि मैं एक समाधान लागू करता हूं जो RTTI का उपयोग नहीं करता है, तो इसका आमतौर पर मतलब है कि मुझे इस जानकारी को संग्रहीत करने के लिए अपनी कक्षाओं में कुछ क्षेत्रों को जोड़ना होगा, इसलिए मेमोरी तर्क शून्य है (मैं आगे इस का एक उदाहरण दूंगा)।

एक डायनामिक_कास्ट धीमा हो सकता है, वास्तव में। हालांकि आमतौर पर इसे गति-गंभीर स्थितियों का उपयोग करने से बचने के तरीके हैं। और मैं विकल्प नहीं देखता। यह SO उत्तर , प्रकार को संग्रहीत करने के लिए आधार वर्ग में परिभाषित एक एनुम का उपयोग करने का सुझाव देता है। यह तभी काम करता है जब आप अपने सभी व्युत्पन्न वर्गों को प्राथमिकता देते हैं। वह काफी बड़ा "अगर" है!

उस जवाब से, यह भी लगता है कि आरटीटीआई की लागत स्पष्ट नहीं है, या तो। अलग-अलग लोग अलग-अलग चीजें मापते हैं।

सुरुचिपूर्ण बहुरूपी डिजाइन RTTI को अनावश्यक बना देंगे

इस तरह की सलाह मैं गंभीरता से लेता हूं। इस मामले में, मैं केवल अच्छे गैर-आरटीटीआई समाधान के साथ नहीं आ सकता हूं जो मेरे आरटीटीआई उपयोग के मामले को कवर करते हैं। मुझे एक उदाहरण प्रदान करें:

कहते हैं कि मैं कुछ प्रकार की वस्तुओं के ग्राफ़ को संभालने के लिए एक पुस्तकालय लिख रहा हूं। मैं अपने पुस्तकालय का उपयोग करते समय उपयोगकर्ताओं को अपने स्वयं के प्रकार उत्पन्न करने की अनुमति देना चाहता हूं (इसलिए एनम विधि उपलब्ध नहीं है)। मेरे पास अपने नोड के लिए एक बेस क्लास है:

class node_base
{
  public:
    node_base();
    virtual ~node_base();

    std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};

अब, मेरे नोड विभिन्न प्रकार के हो सकते हैं। इनके बारे में क्या ख्याल है:

class red_node : virtual public node_base
{
  public:
    red_node();
    virtual ~red_node();

    void get_redness();
};

class yellow_node : virtual public node_base
{
  public:
    yellow_node();
    virtual ~yellow_node();

    void set_yellowness(int);
};

नरक, इनमें से एक भी क्यों नहीं:

class orange_node : public red_node, public yellow_node
{
  public:
    orange_node();
    virtual ~orange_node();

    void poke();
    void poke_adjacent_oranges();
};

अंतिम समारोह दिलचस्प है। यहाँ इसे लिखने का एक तरीका दिया गया है:

void orange_node::poke_adjacent_oranges()
{
    auto adj_nodes = get_adjacent_nodes();
    foreach(auto node, adj_nodes) {
        // In this case, typeid() and static_cast might be faster
        std::shared_ptr<orange_node> o_node = dynamic_cast<orange_node>(node);
        if (o_node) {
             o_node->poke();
        }
    }
}

यह सब स्पष्ट और साफ लगता है। मुझे उन विशेषताओं या विधियों को परिभाषित करने की आवश्यकता नहीं है जहाँ मुझे उनकी आवश्यकता नहीं है, बेस नोड वर्ग दुबला और क्षुद्र रह सकता है। RTTI के बिना, मैं कहाँ से शुरू करूँ? हो सकता है कि मैं एक base_type विशेषता को आधार वर्ग में जोड़ सकूँ:

class node_base
{
  public:
    node_base();
    virtual ~node_base();

    std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();

  private:
    std::string my_type;
};

क्या std :: स्ट्रिंग एक प्रकार के लिए एक अच्छा विचार है? शायद नहीं, लेकिन मैं और क्या उपयोग कर सकता हूं? एक संख्या बनाएं और आशा करें कि कोई और इसका उपयोग नहीं कर रहा है? इसके अलावा, मेरे नारंगी_नोड के मामले में, क्या होगा अगर मैं red_node और yellow_node से विधियों का उपयोग करना चाहता हूं? क्या मुझे प्रति नोड कई प्रकार के स्टोर करने होंगे? यह जटिल लगता है।

निष्कर्ष

यह उदाहरण अत्यधिक जटिल या असामान्य नहीं लगता है (मैं अपने दिन के काम में कुछ इसी तरह काम कर रहा हूं, जहां नोड वास्तविक हार्डवेयर का प्रतिनिधित्व करते हैं जो सॉफ्टवेयर के माध्यम से नियंत्रित हो जाता है, और जो कि वे क्या हैं, उसके आधार पर बहुत अलग बात करते हैं)। फिर भी मुझे टेम्पलेट या अन्य तरीकों से ऐसा करने का एक साफ तरीका नहीं पता होगा। कृपया ध्यान दें कि मैं समस्या को समझने की कोशिश कर रहा हूं, न कि अपने उदाहरण का बचाव करने के लिए। मेरे जैसे पेजों के पढ़ने से एसओ उत्तर मैं ऊपर जुड़ा हुआ है और विकीबूक पर यह पृष्ठ लगता है कि मैं आरटीटीआई का दुरुपयोग कर रहा हूं, लेकिन मैं सीखना चाहूंगा कि क्यों।

इसलिए, मेरे मूल प्रश्न पर वापस जाएं: RTTI का उपयोग करने पर 'शुद्ध बहुरूपता' क्यों बेहतर है?


9
अपने प्रहार संतरे उदाहरण को हल करने के लिए आप "लापता" (भाषा की विशेषता के रूप में) कई प्रेषण ("मल्टीमिथोड्स") होंगे। इस प्रकार, अनुकरण करने के तरीकों की तलाश है जो एक विकल्प हो सकता है। आमतौर पर, विज़िटर पैटर्न का उपयोग किया जाता है।
डैनियल जर्स

1
प्रकार के रूप में एक स्ट्रिंग का उपयोग करना बहुत उपयोगी नहीं है। कुछ "प्रकार" वर्ग के उदाहरण के लिए पॉइंटर का उपयोग करने से यह तेज़ हो जाएगा। लेकिन तब आप मूल रूप से मैन्युअल रूप से आरटीटीआई कर रहे होंगे।
डैनियल जर्स

4
@MargaretBloom नहीं, यह नहीं होगा, RTTI रनटाइम प्रकार की जानकारी के लिए है जबकि CRTP केवल टेम्पलेट्स के लिए है - स्थिर प्रकार, इसलिए।
edmz

2
@ mbr0wn: सभी इंजीनियरिंग प्रक्रियाएँ कुछ नियमों से बंधी होती हैं; प्रोग्रामिंग अपवाद नहीं है। नियमों को दो बाल्टी में विभाजित किया जा सकता है: नरम नियम (SHOULD) और कठोर नियम (MUST)। (बोलने के लिए एक सलाह / विकल्प बाल्टी (COULD) भी है।) यह पढ़ें कि C / C ++ मानक (या इस तथ्य के लिए कोई अन्य मानक। मानक) को कैसे परिभाषित करता है। मैं आपकी समस्या को सच है कि आप गलत "RTTI का उपयोग नहीं करते" एक के रूप में है से आता है लगता है कि कठिन नियम ( "आप RTTI का उपयोग musn't")। यह वास्तव में एक नरम नियम है ("आप SHOULDN'T उपयोग RTTI"), जिसका अर्थ है कि आपको जब भी संभव हो, इससे बचना

3
मैं बहुत सारे जवाबों पर ध्यान नहीं देता हूं कि इस विचार पर ध्यान न दें कि आपका उदाहरण node_baseएक पुस्तकालय का हिस्सा है और उपयोगकर्ता अपने स्वयं के नोड प्रकार बनाएंगे। फिर वे एक और समाधान की अनुमति देने के लिए संशोधित नहीं कर सकतेnode_base हैं, इसलिए हो सकता है कि RTTI फिर उनका सबसे अच्छा विकल्प बन जाए। दूसरी ओर, इस तरह के पुस्तकालय को डिजाइन करने के अन्य तरीके हैं ताकि नए नोड प्रकार आरटीटीआई (और नए नोड प्रकारों को भी डिजाइन करने के अन्य तरीके) का उपयोग किए बिना बहुत अधिक सुरुचिपूर्ण ढंग से फिट हो सकें।
मैथ्यू वाल्टन

जवाबों:


69

एक इंटरफ़ेस बताता है कि कोड में किसी दिए गए स्थिति में बातचीत करने के लिए किसी को क्या पता होना चाहिए। एक बार जब आप "अपने पूरे प्रकार के पदानुक्रम" के साथ इंटरफ़ेस का विस्तार करते हैं, तो आपका इंटरफ़ेस "सतह क्षेत्र" बहुत बड़ा हो जाता है, जो इसके बारे में तर्क कठिन बनाता है ।

एक उदाहरण के रूप में, आपके "आसन्न संतरे" का अर्थ है कि मैं, एक 3 पार्टी के रूप में, नारंगी होने का अनुकरण नहीं कर सकता! आपने निजी रूप से एक नारंगी प्रकार घोषित किया है, तो उस प्रकार के साथ बातचीत करते समय अपने कोड को विशेष बनाने के लिए RTTI का उपयोग करें। यदि मैं "नारंगी" होना चाहता हूं, तो मुझे आपके निजी बगीचे के भीतर होना चाहिए।

अब हर कोई जो आपके पूरे नारंगी प्रकार के साथ "संज्ञा" वाले जोड़े हैं, और परिभाषित इंटरफ़ेस के बजाय, आपके पूरे निजी बगीचे के साथ।

जबकि पहली नज़र में यह सभी क्लाइंट (जोड़ने am_I_orange) को बदलने के बिना सीमित इंटरफ़ेस का विस्तार करने के लिए एक शानदार तरीका दिखता है, इसके बजाय क्या होता है यह कोड आधार को ossify करता है, और आगे के विस्तार को रोकता है । विशेष संतरे प्रणाली के कामकाज के लिए अंतर्निहित हो जाते हैं, और आपको नारंगी के लिए "टेंजेरीन" प्रतिस्थापन बनाने से रोकता है जो अलग तरीके से लागू किया जाता है और शायद एक निर्भरता को हटा देता है या किसी अन्य समस्या को हल करता है।

इसका मतलब है कि आपकी समस्या को हल करने के लिए आपका इंटरफ़ेस पर्याप्त होना चाहिए। उस दृष्टिकोण से, आपको केवल प्रचलित संतरे की आवश्यकता क्यों है, और यदि ऐसा है तो इंटरफ़ेस में अनुपलब्धता क्यों अनुपलब्ध थी? यदि आपको टैग के कुछ फ़ज़ी सेट की ज़रूरत है, जो तदर्थ में जोड़े जा सकते हैं, तो आप इसे अपने प्रकार में जोड़ सकते हैं:

class node_base {
  public:
    bool has_tag(tag_name);

यह आपके इंटरफ़ेस के समान बड़े पैमाने पर विस्तृत रूप से विस्तृत टैग-आधारित निर्दिष्ट करता है। आरटीआई और कार्यान्वयन विवरण (उर्फ, "आप कैसे लागू किए गए हैं? नारंगी प्रकार के साथ? ओक आप पास?") के माध्यम से इसे करने के बजाय, यह पूरी तरह से अलग कार्यान्वयन के माध्यम से आसानी से उत्सर्जित कुछ के साथ ऐसा करता है।

यह भी गतिशील तरीकों के लिए बढ़ाया जा सकता है, अगर आपको इसकी आवश्यकता है। "क्या आप बाज़, टॉम और ऐलिस के साथ फूएड होने का समर्थन करते हैं? ओके, फ़ूइंग यू।" एक बड़े अर्थ में, यह इस तथ्य को प्राप्त करने के लिए एक गतिशील कलाकारों की तुलना में कम घुसपैठ है कि दूसरी वस्तु एक प्रकार है जिसे आप जानते हैं।

क्रियान्वयन-विघटित होने के दौरान अब कीनू वस्तुओं में नारंगी का टैग और साथ हो सकता है।

यह अभी भी एक बड़ी गड़बड़ी पैदा कर सकता है, लेकिन यह संदेश और डेटा की कम से कम गड़बड़ी है, न कि कार्यान्वयन पदानुक्रम।

अमूर्तता अप्रासंगिकता को छिपाने और छिपाने का खेल है। यह स्थानीय स्तर पर तर्क को आसान बनाता है। RTTI कार्यान्वयन विवरण में अमूर्त के माध्यम से एक छेद को सीधे उबाऊ है। यह एक समस्या को हल करना आसान बना सकता है, लेकिन इसमें आपको एक विशिष्ट कार्यान्वयन में आसानी से लॉक करने की लागत है।


14
बहुत अंतिम पैराग्राफ के लिए +1; केवल इसलिए नहीं कि मैं आपसे सहमत हूँ, बल्कि इसलिए कि यहाँ पर हथौड़ा-पर-कील है।

7
किसी व्यक्ति को उस कार्यक्षमता के समर्थन के रूप में एक बार पता चल जाता है कि किसी वस्तु को विशेष कार्यक्षमता कैसे मिलती है? या तो इसमें कास्टिंग शामिल है, या हर संभव सदस्य फ़ंक्शन के साथ भगवान वर्ग है। पहली संभावना या तो अनियंत्रित कास्टिंग है, जिस स्थिति में टैगिंग केवल एक बहुत ही गतिशील गतिशील जाँच योजना है, या यह जाँच की जाती है dynamic_cast(RTTI), जिस स्थिति में टैग निरर्थक हैं। दूसरी संभावना, एक ईश्वर वर्ग, घृणास्पद है। संक्षेप में, इस उत्तर में कई शब्द हैं जो मुझे लगता है कि जावा प्रोग्रामर के लिए अच्छा लगता है, लेकिन वास्तविक सामग्री अर्थहीन है।
चीयर्स एंड हीथ। - अल्फ

2
@ फाल्को: यह (एक प्रकार का) पहली संभावना है जिसका मैंने उल्लेख किया, टैग के आधार पर अनियंत्रित कास्टिंग। यहाँ टैगिंग बहुत ही भंगुर और बहुत ही पतनशील गतिशील प्रकार की जाँच योजना है। कोई भी छोटा ग्राहक कोड दुर्व्यवहार करता है, और C ++ में UB- भूमि में बंद है। आपको अपवाद नहीं मिलते हैं, जैसा कि आप जावा में प्राप्त कर सकते हैं, लेकिन अपरिभाषित व्यवहार, जैसे क्रैश, और / या गलत परिणाम। बहुत अधिक अविश्वसनीय और खतरनाक होने के अलावा, यह बहुत ही अक्षम है, और अधिक समझदार C ++ कोड की तुलना में। IOW।, यह बहुत ही असहनीय है; बहुत हो गया।
चीयर्स एंड हीथ। - अल्फ

1
Uhm। :) तर्क प्रकार?
चीयर्स एंड हीथ। -

2
@JOOatXGME: क्योंकि "बहुरूपता" का अर्थ विभिन्न प्रकार के साथ काम करने में सक्षम होना है। यदि आपको यह जांचना है कि क्या यह एक विशेष प्रकार है, तो पहले से मौजूद प्रकार की जाँच से परे, जिसका उपयोग आपने पॉइंटर / संदर्भ के साथ शुरू करने के लिए किया था, तो आप बहुरूपता के पीछे लग रहे हैं। आप विभिन्न प्रकारों के साथ काम नहीं कर रहे हैं; आप एक विशिष्ट प्रकार के साथ काम कर रहे हैं । हां, जावा में "(बड़े) प्रोजेक्ट हैं" जो ऐसा करते हैं। लेकिन वह जावा है ; भाषा केवल गतिशील बहुरूपता की अनुमति देती है। C ++ में स्थैतिक बहुरूपता भी है। इसके अलावा, सिर्फ इसलिए कि कोई "बड़ा" करता है वह इसे एक अच्छा विचार नहीं बनाता है।
निकोल बोलस

31

इस या उस विशेषता के खिलाफ सबसे अधिक नैतिक आत्महत्या की विशेषता यह है कि इस विशेषता से यह पता चलता है कि उस सुविधा के गलत उपयोग की एक संख्या है।

जहां नैतिकतावादी विफल होते हैं, वे मानते हैं कि सभी उपयोग गलत हैं, जबकि वास्तव में एक कारण के लिए सुविधाएँ मौजूद हैं।

उन्हें लगता है कि सब: वे क्या मैं "प्लंबर जटिल" कॉल करने के लिए इस्तेमाल किया नल कर रहे हैं खराबी क्योंकि सभी नल वे मरम्मत करने के लिए कहा जाता है। वास्तविकता यह है कि अधिकांश नल अच्छी तरह से काम करते हैं: आप बस उनके लिए एक प्लम्बर नहीं कहते हैं!

एक पागल चीज जो तब हो सकती है, जब किसी दिए गए फीचर का उपयोग करने से बचने के लिए, प्रोग्रामर बहुत सारे बॉयलरप्लेट कोड लिखते हैं वास्तव में निजी तौर पर उस सुविधा को फिर से लागू करते हैं। (क्या आपने कभी ऐसी कक्षाओं से मुलाकात की है जो आरटीटीआई और वर्चुअल कॉल का उपयोग नहीं करते हैं, लेकिन वास्तविक व्युत्पन्न प्रकार को ट्रैक करने के लिए एक मूल्य है। वे भेष में आरटीटीआई के पुनर्निमाण से अधिक नहीं हैं ।)

बहुरूपता के बारे में सोचने का एक सामान्य तरीका है IF(selection) CALL(something) WITH(parameters):। (क्षमा करें, लेकिन प्रोग्रामिंग, जब अमूर्तता की अवहेलना की जाती है, तो यह सब उसके बारे में है)

डिजाइन-टाइम (अवधारणाओं) का संकलन-समय (टेम्पलेट-कटौती आधारित), रन-टाइम (वंशानुक्रम और आभासी फ़ंक्शन-आधारित) या डेटा-चालित (आरटीटीआई और स्विचिंग) बहुरूपता का उपयोग इस बात पर निर्भर करता है कि निर्णयों में से कितना ज्ञात है। उत्पादन के प्रत्येक चरण में और वे हर संदर्भ में कितने परिवर्तनशील हैं।

विचार यह है कि:

जितना अधिक आप अनुमान लगा सकते हैं, त्रुटियों को पकड़ने का बेहतर मौका होगा और अंतिम-उपयोगकर्ता को प्रभावित करने वाले कीड़े से बचेंगे।

यदि सब कुछ स्थिर है (डेटा सहित) आप टेम्पलेट मेटा-प्रोग्रामिंग के साथ सब कुछ कर सकते हैं। वास्तविक स्थिरांक पर संकलन होने के बाद, पूरा कार्यक्रम सिर्फ एक वापसी बयान के लिए उबलता है जो परिणाम को बाहर निकालता है ।

यदि ऐसे कई मामले हैं जो सभी संकलन समय पर ज्ञात हैं , लेकिन आपको उन वास्तविक डेटा के बारे में नहीं पता है जिन पर उन्हें कार्य करना है, तो संकलन-समय का बहुरूपता (मुख्यतः CRTP या समान) एक समाधान हो सकता है।

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

यदि स्विचिंग बहुआयामी है, क्योंकि C ++ में कोई मूल मल्टीपल रनटाइम प्रेषण मौजूद नहीं है , तो आपको या तो:

  • Goedelization द्वारा एक आयाम को कम करना : यह वह जगह है जहाँ हीरे और स्टैक्ड समानांतर चतुर्भुज के साथ आभासी आधार और कई वंशानुक्रम होते हैं, लेकिन इसके लिए संभव संयोजन की संख्या और अपेक्षाकृत छोटे होने की आवश्यकता होती है।
  • आयामों को एक-दूसरे से मिलाएं (जैसे कि कंपोज़िट-विज़िटर पैटर्न में, लेकिन इसके लिए सभी वर्गों को अपने अन्य भाई-बहनों के बारे में पता होना चाहिए, इस प्रकार यह उस जगह से "पैमाने" नहीं निकाल सकता है जहां से इसकी कल्पना की गई है)
  • कई मूल्यों के आधार पर कॉल को डिस्पैच करें। आरटीटीआई के लिए यही ठीक है।

यदि केवल स्विचिंग नहीं है, लेकिन यहां तक ​​कि कार्यों को संकलित समय ज्ञात नहीं है, तो स्क्रिप्टिंग और पार्सिंग की आवश्यकता है: डेटा को स्वयं उन पर की जाने वाली कार्रवाई का वर्णन करना होगा।

अब, चूंकि मेरे द्वारा दर्ज किए गए प्रत्येक मामले को एक विशेष मामले के रूप में देखा जा सकता है जो इसका अनुसरण करता है, आप नीचे-सबसे समाधान का दुरुपयोग करके हर समस्या को हल कर सकते हैं, साथ ही सबसे ऊपर के साथ सस्ती समस्याओं के लिए भी।

यही नैतिकता वास्तव में बचने के लिए धक्का देती है। लेकिन इसका मतलब यह नहीं है कि सबसे नीचे के डोमेन में रहने वाली समस्याएं मौजूद नहीं हैं!

आरटीटीआई को कोसना सिर्फ उसे कोसने के लिए, उसे कोसने gotoके लिए कोसने जैसा है। तोते के लिए चीजें, प्रोग्रामर नहीं।


स्तरों का एक अच्छा खाता जिस पर प्रत्येक दृष्टिकोण लागू होता है। मैंने हालांकि "Goedelization" के बारे में नहीं सुना है - क्या इसे किसी और नाम से भी जाना जाता है? क्या आप शायद एक लिंक या अधिक स्पष्टीकरण जोड़ सकते हैं? धन्यवाद :)
j_random_hacker 16

1
@j_random_hacker: मैं भी Godelization के इस उपयोग के बारे में उत्सुक हूं। आम तौर पर पहले के रूप में Godelization के बारे में सोचता है, कुछ स्ट्रिंग से कुछ पूर्णांक तक मैपिंग करता है, और दूसरा, इस तकनीक का उपयोग करके औपचारिक भाषाओं में आत्म-संदर्भात्मक बयानों का उत्पादन करता है। मैं आभासी प्रेषण के संदर्भ में इस शब्द से अपरिचित हूं और अधिक जानने के लिए प्यार करूंगा।
एरिक लिपर्ट

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

... जब यह सब एक परिमित क्षेत्र (एक सीमा) के अंदर होता है, तो रैखिक संयोजन अधिक प्रभावी होते हैं (एक मैट्रिक्स के सेल के एक सरणी में सूचकांक प्राप्त करने वाला शास्त्रीय i = r * C + c)। इस मामले में, डिवाइड आईडी "विजिटर" है और विजेता "कम्पोजिट" है। चूंकि रेखीय बीजगणित शामिल है, इस मामले में तकनीक "विकर्णीकरण" के अनुरूप है
एमिलियो गरवाग्लिया

इन सभी को तकनीकों के रूप में न समझें। वे सिर्फ उपमाएँ हैं
एमिलियो गारागेलिया

23

यह एक छोटे से उदाहरण में साफ-सुथरा दिखता है, लेकिन वास्तविक जीवन में आप जल्द ही एक लंबे सेट के साथ समाप्त हो जाएंगे जो एक दूसरे को पोक कर सकते हैं, उनमें से कुछ शायद केवल एक ही दिशा में हैं।

क्या dark_orange_node, या black_and_orange_striped_node, या dotted_node? क्या इसमें विभिन्न रंगों के डॉट्स हो सकते हैं? क्या होगा यदि अधिकांश डॉट्स नारंगी होते हैं, तो क्या इसे पोक किया जा सकता है?

और हर बार जब आपको एक नया नियम जोड़ना होता है, तो आपको सभी poke_adjacentकार्यों को फिर से करना होगा और अधिक अगर-स्टेटमेंट जोड़ना होगा।


हमेशा की तरह, जेनेरिक उदाहरण बनाना कठिन है, मैं आपको वह देता हूं।

लेकिन अगर मुझे यह विशिष्ट उदाहरण करना था, तो मैं poke()सभी वर्गों के लिए एक सदस्य जोड़ूंगा और उनमें से कुछ को कॉल ( void poke() {}) को अनदेखा करने दूंगा यदि वे रुचि नहीं रखते हैं।

निश्चित रूप से यह typeidएस की तुलना की तुलना में कम महंगा होगा ।


3
आप कहते हैं "निश्चित रूप से", लेकिन क्या आपको इतना निश्चित करता है? यह वास्तव में मैं क्या पता लगाने की कोशिश कर रहा हूँ। मान लें कि मैं नारंगी_नोड का नाम बदलकर पोकेबल_नोड रखता हूं, और वे ही हैं जिन्हें मैं पोक () कह सकता हूं। इसका मतलब है कि मेरे इंटरफ़ेस को एक प्रहार () विधि को लागू करने की आवश्यकता होगी जो कहती है, एक अपवाद को फेंकती है ("यह नोड पोकेबल नहीं है")। यह अधिक महंगा लगता है।
mbr0wn

2
उसे एक अपवाद फेंकने की आवश्यकता क्यों होगी? यदि आप परवाह करते हैं कि इंटरफ़ेस "पोक-सक्षम" है या नहीं, तो "फंक्शन ऐड करें" पॉचेबल है "और पोक फ़ंक्शन को कॉल करने से पहले इसे कॉल करें। या बस वही करें जो वह कहता है और "कुछ भी नहीं, गैर-पोकेबल वर्गों में"।
ब्रैंडन

1
@ mbr0wn: बेहतर सवाल यह है कि आप एक ही आधार वर्ग को साझा करने के लिए पोकेबल और नॉनपेकेबल नोड्स क्यों चाहते हैं।
निकोल बोलस

2
@NicolBolas आप दोस्ताना और शत्रुतापूर्ण राक्षसों को एक ही आधार वर्ग, या ध्यान देने योग्य और गैर-ध्यान देने योग्य यूआई तत्वों, या एक कीबोर्ड के बिना एक कीबोर्ड और कीबोर्ड के साथ साझा करना चाहते हैं?
user253751

1
@ mbr0wn यह व्यवहार-पैटर्न जैसा लगता है। बेस इंटरफ़ेस में दो विधियाँ हैं supportsBehaviourऔर invokeBehaviourप्रत्येक वर्ग में व्यवहार सूची हो सकती है। एक व्यवहार पोक होगा और सभी वर्गों द्वारा समर्थित व्यवहारों की सूची में जोड़ा जा सकता है जो पोकेबल होना चाहते हैं।
फाल्को

20

कुछ संकलक इसका उपयोग नहीं करते / RTTI हमेशा सक्षम नहीं होता है

मेरा मानना ​​है कि आपने इस तरह के तर्कों को गलत समझा है।

कई C ++ कोडिंग स्थान हैं जहाँ RTTI का उपयोग नहीं किया जाना है। जहां संकलक स्विच का उपयोग आरटीटीआई को जबरन अक्षम करने के लिए किया जाता है। यदि आप इस तरह के एक प्रतिमान के भीतर कोडिंग कर रहे हैं ... तो आप निश्चित रूप से इस प्रतिबंध के बारे में पहले ही सूचित कर चुके हैं।

इसलिए समस्या पुस्तकालयों के साथ है । यही है, यदि आप एक पुस्तकालय लिख रहे हैं जो RTTI पर निर्भर करता है, तो आपका पुस्तकालय RTTI को बंद करने वाले उपयोगकर्ताओं द्वारा उपयोग नहीं किया जा सकता है। यदि आप चाहते हैं कि आपकी लाइब्रेरी का उपयोग उन लोगों द्वारा किया जाए, तो यह RTTI का उपयोग नहीं कर सकता है, भले ही आपकी लाइब्रेरी का उपयोग उन लोगों द्वारा भी किया जाए जो RTTI का उपयोग कर सकते हैं। समान रूप से महत्वपूर्ण, यदि आप आरटीटीआई का उपयोग नहीं कर सकते हैं, तो आपको पुस्तकालयों के लिए थोड़ी मुश्किल से खरीदारी करनी होगी, क्योंकि आरटीटीआई का उपयोग आपके लिए एक डील-ब्रेकर है।

इसमें अतिरिक्त मेमोरी खर्च होती है / यह धीमी हो सकती है

कई चीजें हैं जो आप गर्म छोरों में नहीं करते हैं। आप स्मृति आवंटित नहीं करते हैं। आप लिंक की गई सूचियों के माध्यम से पुनरावृत्ति नहीं करते हैं। इत्यादि। आरटीटीआई निश्चित रूप से उन लोगों में से एक हो सकता है जो "यहां ऐसा नहीं करते हैं" चीजें।

हालाँकि, अपने सभी RTTI उदाहरणों पर विचार करें। सभी मामलों में, आपके पास अनिश्चित प्रकार के एक या अधिक ऑब्जेक्ट हैं, और आप उन पर कुछ ऑपरेशन करना चाहते हैं जो उनमें से कुछ के लिए संभव नहीं हो सकता है।

यह कुछ ऐसा है जिसे आपको एक डिज़ाइन स्तर पर काम करना होगा । आप उन कंटेनरों को लिख सकते हैं जो स्मृति को आवंटित नहीं करते हैं जो "एसटीएल" प्रतिमान में फिट होते हैं। आप लिंक्ड सूची डेटा संरचनाओं से बच सकते हैं, या उनके उपयोग को सीमित कर सकते हैं। आप सरणियों की संरचना में सरणियों या जो कुछ भी हो, का पुनर्गठन कर सकते हैं। यह कुछ चीजों को बदल देता है, लेकिन आप इसे कंपार्टमेंटलाइज्ड रख सकते हैं।

एक नियमित वर्चुअल फंक्शन कॉल में एक जटिल RTTI ऑपरेशन को बदलना? यह एक डिजाइन मुद्दा है। अगर आपको इसे बदलना है, तो यह ऐसी चीज है जिसके लिए हर व्युत्पन्न वर्ग में बदलाव की आवश्यकता है । यह बदलता है कि विभिन्न वर्गों के साथ कोड का कितना तालमेल है। इस तरह के बदलाव का दायरा कोड के प्रदर्शन-महत्वपूर्ण वर्गों से कहीं अधिक है।

तो ... आपने इसे शुरू करने का गलत तरीका क्यों लिखा?

मुझे उन विशेषताओं या विधियों को परिभाषित करने की आवश्यकता नहीं है जहाँ मुझे उनकी आवश्यकता नहीं है, बेस नोड वर्ग दुबला और क्षुद्र रह सकता है।

किस हद तक?

आप कहते हैं कि आधार वर्ग "दुबला और क्षुद्र" है। लेकिन वास्तव में ... यह कोई नहीं है । यह वास्तव में कुछ भी नहीं करता है

बस अपने उदाहरण को देखो node_base:। यह क्या है? यह एक ऐसी चीज प्रतीत होती है, जिसमें अन्य चीजें समीप हैं। यह एक जावा इंटरफ़ेस (उस पर पूर्व-जेनेरिक जावा) है: एक ऐसा वर्ग जो केवल कुछ ऐसा होता है जिसे उपयोगकर्ता वास्तविक प्रकार में डाल सकते हैं । हो सकता है कि आप आसन्न (जावा जोड़ता ToString) जैसी कुछ बुनियादी सुविधा जोड़ते हैं , लेकिन यह है।

"दुबला और क्षुद्र" और "पारदर्शी" के बीच अंतर है।

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

लेकिन वे जो भी करते हैं, वह वास्तव में नया सामान बनाने के लिए एक बड़ी पीड़ा है, यहां तक ​​कि सिस्टम के भीतर भी। अपने poke_adjacent_orangesकार्य पर विचार करें । यदि कोई व्यक्ति एक lime_nodeप्रकार चाहता है, जो केवल orange_nodes की तरह पोक्ड हो सकता है तो क्या होगा ? खैर, हम इससे नहीं lime_nodeनिकल सकते orange_node; इसका कोई अर्थ नही बन रहा है।

इसके बजाय, हमें एक नया lime_nodeव्युत्पन्न जोड़ना होगा node_base। तब का नाम बदलने poke_adjacent_orangesके लिए poke_adjacent_pokables। और फिर, कास्टिंग करने की कोशिश करें orange_nodeऔर lime_node; जो भी काम करता है वह है जो हम प्रहार करते हैं।

हालांकि, lime_nodeयह है की जरूरत है खुद poke_adjacent_pokables । और इस फ़ंक्शन को एक ही कास्टिंग चेक करने की आवश्यकता है।

और अगर हम एक तीसरा प्रकार जोड़ते हैं, तो हमें न केवल अपना स्वयं का फ़ंक्शन जोड़ना होगा, बल्कि हमें अन्य दो वर्गों में कार्यों को बदलना होगा।

जाहिर है, अब आप poke_adjacent_pokablesएक निशुल्क फ़ंक्शन करते हैं, ताकि यह उन सभी के लिए काम करे। लेकिन आपको क्या लगता है अगर कोई चौथे प्रकार को जोड़ता है और उस फ़ंक्शन में जोड़ना भूल जाता है?

नमस्कार, मौन विच्छेद । कार्यक्रम कम या ज्यादा ठीक काम करता प्रतीत होता है, लेकिन ऐसा नहीं है। था pokeएक किया गया वास्तविक आभासी समारोह, संकलक जब आप से शुद्ध आभासी समारोह पर हावी नहीं था विफल रही है | node_base

आपके रास्ते में, आपके पास ऐसे संकलक चेक नहीं हैं। यकीन है, संकलक गैर-शुद्ध वर्चुअल के लिए जाँच नहीं करेगा, लेकिन कम से कम आपको उन मामलों में सुरक्षा है जहां सुरक्षा संभव है (यानी: कोई डिफ़ॉल्ट ऑपरेशन नहीं है)।

आरटीटीआई के साथ पारदर्शी आधार कक्षाओं का उपयोग एक रखरखाव दुःस्वप्न की ओर जाता है। दरअसल, आरटीटीआई के अधिकांश उपयोग से रखरखाव सिरदर्द होता है। इसका मतलब यह नहीं है कि RTTI उपयोगी नहीं है (यह boost::anyकाम करने के लिए महत्वपूर्ण है , उदाहरण के लिए)। लेकिन यह बहुत विशेष जरूरतों के लिए एक बहुत ही विशेष उपकरण है ।

उस तरह से, यह उसी तरह से "हानिकारक" है goto। यह एक उपयोगी उपकरण है जिसे दूर नहीं किया जाना चाहिए। लेकिन इसका उपयोग आपके कोड के भीतर दुर्लभ होना चाहिए ।


इसलिए, यदि आप पारदर्शी आधार कक्षाओं और गतिशील कास्टिंग का उपयोग नहीं कर सकते हैं, तो आप वसा इंटरफेस से कैसे बचें? आप प्रत्येक फ़ंक्शन को बुदबुदाने से कैसे बचाएंगे जो आप आधार वर्ग तक बुदबुदाती से एक प्रकार पर कॉल करना चाहते हैं?

उत्तर इस बात पर निर्भर करता है कि आधार वर्ग किसके लिए है।

पारदर्शी बेस क्लास जैसे node_baseसमस्या के लिए सिर्फ गलत टूल का उपयोग कर रहे हैं। लिंक की गई सूचियाँ टेम्प्लेट द्वारा सबसे अच्छी तरह से नियंत्रित की जाती हैं। नोड प्रकार और आसन्न एक टेम्पलेट प्रकार द्वारा प्रदान किया जाएगा। यदि आप सूची में एक बहुरूपी प्रकार रखना चाहते हैं, तो आप कर सकते हैं। बस के BaseClass*रूप Tमें टेम्पलेट तर्क में उपयोग करें । या आपका पसंदीदा स्मार्ट पॉइंटर।

लेकिन अन्य परिदृश्य हैं। एक एक प्रकार है जो बहुत कुछ करता है, लेकिन कुछ वैकल्पिक भाग हैं। एक विशेष उदाहरण कुछ कार्यों को लागू कर सकता है, जबकि दूसरा नहीं करेगा। हालांकि, इस प्रकार के डिजाइन आमतौर पर एक उचित उत्तर प्रदान करते हैं।

"संस्था" वर्ग इसका एक आदर्श उदाहरण है। यह वर्ग लंबे समय से खेल डेवलपर्स से ग्रस्त है। वैचारिक रूप से, इसमें एक विशाल इंटरफ़ेस है, जो लगभग एक दर्जन के चौराहे पर रहता है, पूरी तरह से अलग व्यवस्था है। और अलग-अलग संस्थाओं के अलग-अलग गुण होते हैं। कुछ संस्थाओं के पास कोई दृश्य प्रतिनिधित्व नहीं है, इसलिए उनके प्रतिपादन कार्य कुछ नहीं करते हैं। और यह सब रनटाइम पर निर्धारित होता है।

इसके लिए आधुनिक समाधान एक घटक-शैली प्रणाली है। Entityउनके बीच कुछ गोंद के साथ, घटकों के एक सेट का एक कंटेनर मात्र है। कुछ घटक वैकल्पिक हैं; एक ऐसी इकाई जिसका कोई दृश्य प्रतिनिधित्व नहीं है, उसमें "ग्राफिक्स" घटक नहीं है। एआई के साथ एक इकाई का कोई "नियंत्रक" घटक नहीं है। इत्यादि।

इस तरह की प्रणाली में इकाइयाँ घटकों के लिए केवल संकेत हैं, उनके अधिकांश इंटरफ़ेस को घटकों को सीधे एक्सेस करके प्रदान किया जाता है।

इस तरह के एक घटक प्रणाली को विकसित करने के लिए डिजाइन चरण में पहचानने की आवश्यकता होती है, कि कुछ कार्य वैचारिक रूप से एक साथ समूहीकृत होते हैं, जैसे कि सभी प्रकार जो एक को लागू करते हैं, उन सभी को लागू करेगा। यह आपको संभावित आधार वर्ग से कक्षा को निकालने और इसे एक अलग घटक बनाने की अनुमति देता है।

यह एकल उत्तरदायित्व सिद्धांत का पालन करने में भी मदद करता है। ऐसे संघटित वर्ग के पास केवल घटकों के धारक होने की जिम्मेदारी होती है।


मैथ्यू वाल्टन से:

मैं बहुत से जवाबों पर ध्यान देता हूं कि इस विचार पर ध्यान न दें कि आपका उदाहरण बताता है कि नोड_बेस एक पुस्तकालय का हिस्सा है और उपयोगकर्ता अपने स्वयं के नोड प्रकार बनाएंगे। फिर वे एक और समाधान की अनुमति देने के लिए नोड_बेस को संशोधित नहीं कर सकते हैं, इसलिए हो सकता है कि आरटीटीआई उनका सबसे अच्छा विकल्प बन जाए।

ठीक है, आइए देखें।

यह समझने के लिए, आपके पास जो कुछ होना चाहिए वह एक ऐसी स्थिति है जहां कुछ लाइब्रेरी एल एक कंटेनर या अन्य संरचित धारक डेटा प्रदान करता है। उपयोगकर्ता को इस कंटेनर में डेटा जोड़ने के लिए मिलता है, इसकी सामग्री पर पुनरावृति करता है, हालांकि, पुस्तकालय वास्तव में इस डेटा के साथ कुछ भी नहीं करता है; यह बस अपने अस्तित्व का प्रबंधन करता है।

लेकिन यह अपने अस्तित्व को भी नष्ट नहीं करता है । इसका कारण यह है कि, यदि आपको ऐसे उद्देश्यों के लिए RTTI का उपयोग करने की उम्मीद है, तो आप ऐसी कक्षाएं बना रहे हैं, जिनसे L अनभिज्ञ है। इसका अर्थ है कि आपका कोड ऑब्जेक्ट को आवंटित करता है और प्रबंधन के लिए इसे L को बंद कर देता है।

अब, ऐसे मामले हैं जहां कुछ इस तरह का एक वैध डिजाइन है। ईवेंट सिगनलिंग / मैसेज पासिंग, थ्रेड-सेफ वर्क क्वीज इत्यादि। सामान्य पैटर्न यह है: कोई व्यक्ति कोड के दो टुकड़ों के बीच एक सेवा कर रहा है जो किसी भी प्रकार के लिए उपयुक्त है, लेकिन सेवा में विशिष्ट प्रकारों के बारे में पता होना आवश्यक नहीं है ।

सी में, इस पैटर्न की वर्तनी है void*, और इसके उपयोग को तोड़ने से बचने के लिए बहुत अधिक देखभाल की आवश्यकता है। C ++ में, यह पैटर्न वर्तनी वाला है std::experimental::any(जल्द ही वर्तनी हो जाएगा std::any)।

जिस तरह से यह काम करना चाहिए वह यह है कि एल एक node_baseवर्ग प्रदान करता है anyजो आपके वास्तविक डेटा का प्रतिनिधित्व करता है। जब आप संदेश प्राप्त करते हैं, तो थ्रेड कतार कार्य आइटम, या जो कुछ भी आप कर रहे हैं, आप तब anyउसे उसके उपयुक्त प्रकार में डालते हैं, जिसे प्रेषक और रिसीवर दोनों जानते हैं।

तो पाने के बजाय orange_nodeसे node_data, आप बस एक छड़ी orangeके अंदर node_dataके anyसदस्य क्षेत्र। अंतिम-उपयोगकर्ता इसे निकालता है और any_castइसे में परिवर्तित करने के लिए उपयोग करता है orange। यदि कलाकार विफल रहता है, तो यह नहीं था orange

अब, यदि आप इसके कार्यान्वयन से सभी परिचित हैं, तो आप anyकहेंगे, "अरे एक मिनट रुको: काम करने के लिए any आंतरिक रूप से RTTI का उपयोग करता है any_cast।" जिस पर मैं जवाब देता हूं, "... हां"।

यह एक अमूर्तता की बात है । विवरण में गहरी, कोई RTTI का उपयोग कर रहा है। लेकिन जिस स्तर पर आपको काम करना चाहिए, प्रत्यक्ष आरटीआई कुछ ऐसा नहीं है जो आपको करना चाहिए।

आपको उन प्रकारों का उपयोग करना चाहिए जो आपको अपनी कार्यक्षमता प्रदान करते हैं। सब के बाद, आप वास्तव में RTTI नहीं चाहते हैं। आप जो चाहते हैं वह एक डेटा संरचना है जो किसी दिए गए प्रकार के मूल्य को संग्रहीत कर सकता है, वांछित गंतव्य को छोड़कर सभी से इसे छिपा सकता है, फिर उस प्रकार में वापस परिवर्तित किया जा सकता है, सत्यापन के साथ कि संग्रहीत मूल्य वास्तव में उस प्रकार का है।

यही कहा जाता है any। यह आरटीटीआई का उपयोग करता है , लेकिन anyसीधे आरटीटीआई का उपयोग करना कहीं बेहतर है, क्योंकि यह वांछित शब्दार्थ को अधिक सही ढंग से फिट करता है।


10

यदि आप किसी फ़ंक्शन को कॉल करते हैं, तो एक नियम के रूप में आप वास्तव में परवाह नहीं करते हैं कि यह क्या सटीक कदम उठाएगा, केवल कुछ उच्च-स्तरीय लक्ष्य कुछ बाधाओं के भीतर प्राप्त किया जाएगा (और फ़ंक्शन कैसे होता है यह वास्तव में अपनी समस्या है)।

जब आप आरटीटीआई का उपयोग उन विशेष वस्तुओं का चयन करने के लिए करते हैं जो एक निश्चित कार्य कर सकते हैं, जबकि अन्य एक ही सेट में नहीं कर सकते हैं, तो आप दुनिया के उस आरामदायक दृष्टिकोण को तोड़ रहे हैं। अचानक फोन करने वाले को यह पता चल जाता है कि कौन क्या कर सकता है, बजाय इसके कि वह अपने minions को बताए। कुछ लोग इससे परेशान हैं, और मुझे संदेह है कि यह इस कारण का एक बड़ा हिस्सा है कि आरटीटीआई को थोड़ा गंदा क्यों माना जाता है।

क्या कोई प्रदर्शन मुद्दा है? हो सकता है, लेकिन मैंने कभी इसका अनुभव नहीं किया है, और यह बीस साल पहले से, या उन लोगों से ज्ञान हो सकता है जो ईमानदारी से मानते हैं कि दो के बजाय तीन विधानसभा निर्देशों का उपयोग करना अस्वीकार्य ब्लोट है।

तो इससे कैसे निपटा जाए ... आपकी स्थिति के आधार पर अलग-अलग ऑब्जेक्ट्स (यानी संपूर्ण 'ऑरेंज' एपीआई एक अलग ऑब्जेक्ट हो सकता है) में बंडल किए गए किसी भी नोड-विशिष्ट गुणों के होने का मतलब हो सकता है। रूट ऑब्जेक्ट तब 'ऑरेंज' एपीआई को वापस करने के लिए एक आभासी फ़ंक्शन हो सकता है, गैर-नारंगी वस्तुओं के लिए डिफ़ॉल्ट रूप से नल्टरप्रेट कर सकता है।

हालांकि यह आपकी स्थिति के आधार पर ओवरकिल हो सकता है, यह आपको रूट स्तर पर क्वेरी करने की अनुमति देगा कि क्या एक विशिष्ट नोड एक विशिष्ट एपीआई का समर्थन करता है, और यदि ऐसा होता है, तो उस एपीआई के लिए विशिष्ट कार्यों को निष्पादित करता है।


6
पुन: प्रदर्शन लागत - मैंने डायनामिक_कास्ट <> 3 जीएचजेड प्रोसेसर पर हमारे ऐप में लगभग 2 in की लागत को मापा, जो एक एनम की जांच करने की तुलना में लगभग 1000x धीमा है। (हमारे ऐप में 11.1ms मुख्य लूप की समय सीमा है, इसलिए हम माइक्रोसेकंड के बारे में बहुत परवाह करते हैं।)
8

6
कार्यान्वयन के बीच प्रदर्शन में बहुत अंतर होता है। GCC एक टाइपिनफो पॉइंटर तुलना का उपयोग करता है जो तेज है। MSVC स्ट्रिंग तुलनाओं का उपयोग करता है जो तेज नहीं हैं। हालांकि, MSVC की विधि पुस्तकालयों, स्थिर या DLL के विभिन्न संस्करणों से जुड़े कोड के साथ काम करेगी, जहां जीसीसी की पॉइंटर विधि का मानना ​​है कि एक स्थिर पुस्तकालय में एक वर्ग साझा पुस्तकालय में एक वर्ग से अलग है।
ज़ेन लिंक्स

1
@Crashworks बस यहाँ एक पूरा रिकॉर्ड है: कौन सा संकलक (और कौन सा संस्करण) था?
एच। गुइज़्ट

@ क्रेकर्स ने जानकारी के अनुरोध को सुरक्षित किया है जिस पर संकलक ने आपके देखे गए परिणामों का उत्पादन किया; धन्यवाद।
अंडरस्कोर_ड

@underscore_d: MSVC
क्रैशवर्क

9

C ++ को स्टैटिक टाइप चेकिंग के विचार पर बनाया गया है।

[१] आरटीटीआई, dynamic_castऔर type_id, गतिशील प्रकार की जाँच है।

तो, अनिवार्य रूप से आप पूछ रहे हैं कि स्थिर प्रकार की जाँच गतिशील प्रकार की जाँच के लिए बेहतर क्यों है। और सरल उत्तर यह है कि क्या स्थैतिक प्रकार की जाँच गतिशील प्रकार की जाँच के लिए बेहतर है, निर्भर करता है । बहुत पर। लेकिन C ++ उन प्रोग्रामिंग भाषाओं में से एक है जो स्टैटिक टाइप चेकिंग के विचार के आसपास डिज़ाइन की गई हैं। और इसका मतलब यह है कि उदाहरण के लिए, विशेष रूप से परीक्षण में विकास प्रक्रिया, आमतौर पर स्थिर प्रकार की जाँच के लिए अनुकूलित होती है, और फिर वह सबसे अच्छी तरह से फिट होती है।


फिर से

" मुझे टेम्पलेट या अन्य तरीकों से ऐसा करने का एक साफ तरीका नहीं पता होगा

आप इस प्रक्रिया-विषम-नोड्स-ऑफ-द-ग्राफ के साथ स्थिर प्रकार की जाँच कर सकते हैं और आगंतुक पैटर्न के माध्यम से कोई भी कास्टिंग नहीं कर सकते हैं, जैसे:

#include <iostream>
#include <set>
#include <initializer_list>

namespace graph {
    using std::set;

    class Red_thing;
    class Yellow_thing;
    class Orange_thing;

    struct Callback
    {
        virtual void handle( Red_thing& ) {}
        virtual void handle( Yellow_thing& ) {}
        virtual void handle( Orange_thing& ) {}
    };

    class Node
    {
    private:
        set<Node*> connected_;

    public:
        virtual void call( Callback& cb ) = 0;

        void connect_to( Node* p_other )
        {
            connected_.insert( p_other );
        }

        void call_on_connected( Callback& cb )
        {
            for( auto const p : connected_ ) { p->call( cb ); }
        }

        virtual ~Node(){}
    };

    class Red_thing
        : public virtual Node
    {
    public:
        void call( Callback& cb ) override { cb.handle( *this ); }

        auto redness() -> int { return 255; }
    };

    class Yellow_thing
        : public virtual Node
    {
    public:
        void call( Callback& cb ) override { cb.handle( *this ); }
    };

    class Orange_thing
        : public Red_thing
        , public Yellow_thing
    {
    public:
        void call( Callback& cb ) override { cb.handle( *this ); }

        void poke() { std::cout << "Poked!\n"; }

        void poke_connected_orange_things()
        {
            struct Poker: Callback
            {
                void handle( Orange_thing& obj ) override
                {
                    obj.poke();
                }
            } poker;

            call_on_connected( poker );
        }
    };
}  // namespace graph

auto main() -> int
{
    using namespace graph;

    Red_thing   r;
    Yellow_thing    y1, y2;
    Orange_thing    o1, o2, o3;

    for( Node* p : std::initializer_list<Node*>{ &y1, &y2, &r, &o2, &o3 } )
    {
        o1.connect_to( p );
    }
    o1.poke_connected_orange_things();
}

यह मानता है कि नोड प्रकारों का सेट ज्ञात है।

जब ऐसा नहीं होता है, तो आगंतुक पैटर्न (इसके कई रूप हैं) कुछ केंद्रीकृत जातियों के साथ व्यक्त किए जा सकते हैं, या, बस एक ही।


टेम्पलेट-आधारित दृष्टिकोण के लिए बूस्ट ग्राफ़ लाइब्रेरी देखें। दुख की बात है कि मैं इससे परिचित नहीं हूं, मैंने इसका इस्तेमाल नहीं किया है। तो मुझे यकीन नहीं है कि यह क्या और कैसे करता है, और किस हद तक यह RTTI के बजाय स्थैतिक प्रकार की जाँच का उपयोग करता है, लेकिन चूंकि बूस्ट आम तौर पर केंद्रीय विचार के रूप में स्थैतिक प्रकार की जाँच के साथ टेम्पलेट-आधारित है, मुझे लगता है कि आप पाएंगे इसकी ग्राफ उप-पुस्तकालय भी स्थैतिक प्रकार की जाँच पर आधारित है।


[१] रन टाइम टाइप की जानकारी


1
नोट करने के लिए एक "मज़ेदार बात" यह है कि आगंतुक पैटर्न के लिए आवश्यक कोड (परिवर्तनों को जोड़ते समय) की मात्रा को कम कर सकता है, आरटीटीआई का उपयोग करके एक पदानुक्रम "चढ़ाई" कर सकता है। मैं इसे "एसाइक्लिक विजिटर पैटर्न" के रूप में जानता हूं।
डैनियल जर्स

3

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

if ( typeid(5) == "int" )
    // may be false

if ( typeid(5) == typeid(int) )
   // always true

वही हैश के लिए धारण करता है।

[...] RTTI "हानिकारक माना जाता है"

हानिकारक निश्चित रूप से आकलन किया जाता है: RTTI कुछ कमियां है, लेकिन यह करता है फायदे भी हैं।

आपको वास्तव में RTTI का उपयोग नहीं करना है। RTTI OOP समस्याओं को हल करने के लिए एक उपकरण है: क्या आपको एक और प्रतिमान का उपयोग करना चाहिए, ये संभवतः गायब हो जाएंगे। C में RTTI नहीं है, लेकिन फिर भी यह काम करता है। सी ++ इसके बजाय पूरी तरह से ओओपी का समर्थन करता है और आपको कुछ मुद्दों को दूर करने के लिए कई उपकरण देता है जिन्हें रनटाइम जानकारी की आवश्यकता हो सकती है: उनमें से एक वास्तव में आरटीटीआई है, जो हालांकि कीमत के साथ आता है। यदि आप इसे बर्दाश्त नहीं कर सकते हैं, तो सुरक्षित प्रदर्शन विश्लेषण के बाद ही आप बेहतर स्थिति में होंगे, अभी भी पुराने स्कूल हैं void*: यह मुफ़्त है। बिना लागत के। लेकिन आपको कोई प्रकार की सुरक्षा नहीं मिलती है। तो यह सब ट्रेडों के बारे में है।


  • कुछ संकलक उपयोग नहीं करते / RTTI हमेशा सक्षम नहीं होता है
    मैं वास्तव में इस तर्क को नहीं खरीदता हूं। यह कहने की तरह है कि मुझे C ++ 14 सुविधाओं का उपयोग नहीं करना चाहिए, क्योंकि वहाँ बाहर संकलक हैं जो इसका समर्थन नहीं करते हैं। और फिर भी, कोई भी मुझे C ++ 14 सुविधाओं का उपयोग करने से हतोत्साहित नहीं करेगा।

यदि आप लिखते हैं (संभवतः सख्ती से) सी ++ कोड के अनुरूप, तो आप कार्यान्वयन की परवाह किए बिना समान व्यवहार की अपेक्षा कर सकते हैं। मानक-अनुरूप कार्यान्वयन मानक C ++ सुविधाओं का समर्थन करेंगे।

लेकिन विचार करें कि कुछ वातावरणों में C ++ परिभाषित करता है («फ्रीस्टैंडिंग» वाले), RTTI प्रदान करने की आवश्यकता नहीं है और न ही कुछ अपवाद हैं, virtualऔर इसी तरह। आरटीटीआई को सही ढंग से काम करने के लिए एक अंतर्निहित परत की आवश्यकता होती है जो निम्न स्तर के विवरण जैसे एबीआई और वास्तविक प्रकार की जानकारी से संबंधित है।


मैं इस मामले में आरटीटीआई के बारे में याक से सहमत हूं। हाँ, इसका उपयोग किया जा सकता है; लेकिन क्या यह तार्किक रूप से सही है? तथ्य यह है कि भाषा आपको इस चेक को बायपास करने की अनुमति देती है इसका मतलब यह नहीं है कि यह किया जाना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.