2012-08-01 12 views
49

मेरे पास dynamic_cast ऑपरेटर के बारे में एक बहुत ही सरल सवाल है। मुझे पता है कि यह रन टाइम टाइप पहचान के लिए प्रयोग किया जाता है, यानी, रन टाइम पर ऑब्जेक्ट प्रकार के बारे में जानने के लिए। लेकिन आपके प्रोग्रामिंग अनुभव से, क्या आप एक वास्तविक परिदृश्य दे सकते हैं जहां आपको इस ऑपरेटर का उपयोग करना था? इसका उपयोग किए बिना कठिनाइयों क्या थे?गतिशील_कास्ट का व्यावहारिक उपयोग?

+2

अच्छा प्रश्न: मैं विशेष रूप से इवेंट मैनेजमेंट के लिए पर्यवेक्षक पैटर्न साथ उपयोग करें। +1! – Linuxios

उत्तर

49

खिलौना उदाहरण

नोआ संदूक जानवरों के विभिन्न प्रकार के लिए एक कंटेनर के रूप में कार्य करेगा: यहाँ क्या एक "प्रतिपादन" विधि नज़र आ सकते हैं। जैसा कि जहाज में ही बंदरों, पेंगुइन, और मच्छरों के बीच अंतर के बारे में चिंतित नहीं है, आप एक वर्ग Animal परिभाषित वर्गों Monkey, Penguin निकाले जाते हैं, और Mosquito इसे से, और जहाज में एक Animal के रूप में उनमें से प्रत्येक की दुकान।

एक बार बाढ़ खत्म होने के बाद, नूह पृथ्वी पर जानवरों को उन स्थानों पर वितरित करना चाहता है जहां वे हैं और इसलिए उनके जहाज में संग्रहीत सामान्य जानवरों के बारे में अतिरिक्त जानकारी की आवश्यकता है। एक उदाहरण के रूप में, अब यह पता लगाने के लिए कि अंटार्कटिक में कौन से जानवर पेंगुइन जारी किए जाएंगे और जो नहीं हैं, वे अब प्रत्येक जानवर को Penguin पर आज़मा सकते हैं।

वास्तविक जीवन उदाहरण

हम एक घटना निगरानी ढांचे, जहां एक आवेदन एक सूची में क्रम-उत्पन्न घटनाओं संग्रहीत करेंगे कार्यान्वित किया। इवेंट मॉनीटर इस सूची के माध्यम से जाएंगे और उन विशिष्ट घटनाओं की जांच करेंगे जिन्हें वे रुचि रखते थे।इवेंट प्रकार ओएस-स्तरीय चीजें जैसे SYSCALL, FUNCTIONCALL, और INTERRUPT थे।

यहां, हमने Event उदाहरणों की एक सामान्य सूची में हमारे सभी विशिष्ट कार्यक्रमों को संग्रहीत किया। इसके बाद मॉनीटर इस सूची और dynamic_cast<> पर उन घटनाओं को फिर से देखेगा, जिनकी वे रुचि रखते थे। अन्य सभी (जो अपवाद उठाते हैं) को अनदेखा कर दिया जाता है।

प्रश्न: आपके पास प्रत्येक ईवेंट प्रकार के लिए अलग सूची क्यों नहीं हो सकती है?

उत्तर: आप ऐसा कर सकते हैं, लेकिन क्योंकि हर कोई संबंधित सूचियों के लिए जाँच करने के बारे में पता करने की जरूरत है यह मुश्किल नई घटनाओं के साथ-साथ नई पर नज़र रखता है (कई घटना प्रकार के योग) के साथ प्रणाली का विस्तार करता है,।

+1

यह घटना निगरानी वास्तव में बहुत व्यावहारिक उदाहरण थी, मैं स्पष्ट रूप से कल्पना कर सकता था कि गतिशील कलाकार का महत्व क्या है। बहुत बहुत धन्यवाद!!! – cexplorer

+2

महान जवाब! +1! – Linuxios

+4

मुझे खिलौना उदाहरण पसंद है :) +1 –

3

इस स्थिति की कल्पना करें: आपके पास एक सी ++ प्रोग्राम है जो HTML को पढ़ता है और प्रदर्शित करता है। आपके पास बेस क्लास HTMLElement है जिसमें शुद्ध वर्चुअल विधि displayOnScreen है। आपके पास renderHTMLToBitmap नामक फ़ंक्शन भी है, जो HTML को बिटमैप पर खींचता है। यदि प्रत्येक HTMLElement में vector<HTMLElement*> children; है, तो आप HTMLElement तत्व <html> का प्रतिनिधित्व कर सकते हैं। लेकिन क्या होगा यदि कुछ उप-वर्गों को विशेष उपचार की आवश्यकता है, जैसे <link> सीएसएस जोड़ने के लिए। आपको यह जानने का एक तरीका चाहिए कि कोई तत्व LinkElement है ताकि आप इसे सीएसएस फ़ंक्शंस में दे सकें। इसे खोजने के लिए, आप dynamic_cast का उपयोग करेंगे।

dynamic_cast और सामान्य रूप से बहुरूपता के साथ समस्या यह है कि यह बहुत कुशल नहीं है। जब आप मिश्रण में vtables जोड़ते हैं, तो यह केवल खराब हो जाता है।

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

संपादित करें: एंड्रयू टिप्पणी bellow के जवाब में, यहाँ एक नया दृष्टिकोण है: गतिशील कास्टिंग के बजाय विशिष्ट तत्व प्रकार (LinkElement) करने के बजाय आप ActionElement बुलाया HTMLElement का एक और अमूर्त उपवर्ग है कि एक समारोह के साथ displayOnScreen ओवरराइड करता है कुछ भी नहीं प्रदर्शित करता है कि , और एक नया शुद्ध वर्चुअल फ़ंक्शन बनाता है: virtual void doAction() const = 0dynamic_castActionElement के लिए परीक्षण में बदल दिया गया है और बस doAction() पर कॉल करें। आपके पास वर्चुअल विधि displayOnScreen() के साथ GraphicalElement के लिए एक ही प्रकार का सबक्लास होगा।

संपादित करें 2:

void render(HTMLElement root) { 
    for(vector<HTLMElement*>::iterator i = root.children.begin(); i != root.children.end(); i++) { 
    if(dynamic_cast<ActionElement*>(*i) != NULL) //Is an ActionElement 
    { 
     ActionElement* ae = dynamic_cast<ActionElement*>(*i); 
     ae->doAction(); 
     render(ae); 
    } 
    else if(dynamic_cast<GraphicalElement*>(*i) != NULL) //Is a GraphicalElement 
    { 
     GraphicalElement* ge = dynamic_cast<GraphicalElement*>(*i); 
     ge->displayToScreen(); 
     render(ge); 
    } 
    else 
    { 
     //Error 
    } 
    } 
} 
+3

सी ++ पॉलिमॉर्फिज्म और vtables आमतौर पर समान समस्याओं को हल करने के अन्य तरीकों की तुलना में अधिक कुशल है। इन दिनों यह शायद ही कभी चिंता का विषय है, जब तक कि आप एक एम्बेडेड प्लेटफ़ॉर्म या कुछ को लक्षित नहीं कर रहे हों। – aschepler

+0

आपको बहुत लिनक्सियो धन्यवाद !! यह वास्तव में बहुत अच्छा उदाहरण था! मैं अब समझता हूं जब भी हमें कुछ जेनेरिक क्लास तत्व/वैरिएबल का उपयोग अपने व्युत्पन्न लोगों को विभिन्न पॉइंटर्स के साथ करने की आवश्यकता होती है और ऐसे मामले हो सकते हैं जहां हमें विशेष रूप से कुछ व्युत्पन्न लोगों का इलाज करने की आवश्यकता होती है, फिर हम गतिशील कास्ट का उपयोग करते हैं। आपका बहुत बहुत धन्यवाद! लिनक्सियो और BjoernD दोनों के लिए Kudos! – cexplorer

+1

मेरी राय में, आपके द्वारा प्रस्तुत उदाहरण 'dynamic_cast' का अच्छा उपयोग नहीं है। किसी विधि के ठोस प्रकार के लिए परीक्षण करने वाली विधि [ओपन-क्लोज़ेड सिद्धांत] (http://www.objectmentor.com/resources/articles/ocp.pdf) का पालन नहीं करती है। यदि आप अपने पदानुक्रम में नया उप-प्रकार जोड़ते हैं तो क्या होता है? आपको तब अपने कोड के सभी स्थानों को ढूंढना होगा जहां आप विशेष व्यवहार लागू करना चाहते हैं। –

0

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

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

+0

धन्यवाद stefaanv जो आपने समझाया एक अलग पहलू था। – cexplorer

+0

'dynamic_cast' कास्टिंग से अलग है। इसका मतलब यह है कि आप उस जानकारी के लिए कंपाइलर से पूछते हैं जिसे आप नहीं जानते हैं जो आपको पॉलिमॉर्फिज्म में विशिष्ट उप-वर्गों के आधार पर अलग-अलग चीजें करने देगा। – Linuxios

+1

@ लिनक्सियस: मैं असहमत हूं, यह अभी भी – stefaanv

0

ज्यादातर स्थितियों में जहां आप कोड लिख रहे हैं जिसमें आप उस इकाई के प्रकार को जानते हैं जिसके साथ आप काम कर रहे हैं, तो आप static_cast का उपयोग करें क्योंकि यह अधिक कुशल है।

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

आपको फ़ैक्टरी जहां आंतरिक तर्क का फैसला करता है कि किस वर्ग व्युत्पन्न उपयोगकर्ता उपयोगकर्ता स्पष्ट रूप से एक का चयन करने के बजाय चाहता है का उपयोग हो सकता है:

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

आपका अनुवर्ती प्रश्न स्पष्ट रूप से होगा: आपको किसी वस्तु का उपयोग करके कोड में उपयोग करने वाले ऑब्जेक्ट के प्रकार को जानने की आवश्यकता क्यों होगी?

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

यह बदसूरत है, लेकिन यह एक आदर्श दुनिया नहीं है, और कभी-कभी आपके पास काम के दबाव के तहत असली दुनिया में एक अपूर्ण डिजाइन को दोबारा करने का समय नहीं है।

struct Element 
{ 
    virtual ~Element() { } 

    void accept(Visitor & v) 
    { 
     v.visit(this); 
    } 
}; 

struct Visitor 
{ 
    virtual void visit(Element * e) = 0; 
    virtual ~Visitor() { } 
}; 


struct RedElement : Element { }; 
struct BlueElement : Element { }; 
struct FifthElement : Element { }; 


struct MyVisitor : Visitor 
{ 
    virtual void visit(Element * e) 
    { 
     if (RedElement * p = dynamic_cast<RedElement*>(e)) 
     { 
      // do things specific to Red 
     } 
     else if (BlueElement * p = dynamic_cast<BlueElement*>(e)) 
     { 
      // do things specific to Blue 
     } 
     else 
     { 
      // error: visitor doesn't know what to do with this element 
     } 
    } 
}; 

अब अगर आप कुछ Element & e; है, तो आप MyVisitor v; बनाने के लिए और e.accept(v) कह सकते हैं:

+0

हाय W00te, जो बिंदु आप कह रहे हैं वह दिलचस्प है .. क्या आप कृपया बता सकते हैं कि हम उपरोक्त BjoernD द्वारा दिए गए ईवेंट निगरानी उदाहरण में स्थिर कास्ट ऑपरेटर का उपयोग कैसे कर सकते हैं। – cexplorer

+0

@cexplorer: इसे बेस क्लास के किसी प्रकार के 'टाइप' या' आईडी' फ़ील्ड के उपयोग की आवश्यकता होगी जिसे 'स्विच' के साथ चेक किया जाएगा और' dynamic_cast' के साथ कार्य किया जाएगा। मेरे लिए, यह ऐसा लगता है कि संकलक पहले से ही क्या कर चुका है: एक प्रकार का फ़ील्ड बनाएं और यह जानने के लिए पूछें कि क्या आप किसी प्रकार के लिए जा सकते हैं। और संकलक शायद कोड में आपके मुकाबले ज्यादा कुशलता से कर सकता है। – Linuxios

10

एक ठेठ उपयोग के मामले आगंतुक पैटर्न है।

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

+6

ओह, विभिन्न 'एलिमेंट' डेरिवेटिव को स्वीकार करने के लिए 'विज़िट' विधि को अधिभारित करके, आप 'dynamic_cast' की पूरी तरह से आवश्यकता से बच सकते हैं। मुझे लगता है कि विज़िटर पैटर्न का मुख्य लाभ है ('एलिमेंट 'ऑब्जेक्ट्स उनके कंक्रीट प्रकार को उनके आगंतुकों को प्रकट करता है)। –

+2

@AndrewDurward: ओवरलोड रिज़ॉल्यूशन स्थिर रूप से होता है। गतिशील कास्ट गतिशील रूप से होता है। मुझे नहीं लगता कि वे एक ही समस्या को हल करते हैं। आपके दृष्टिकोण को 'एलिमेंट' से व्युत्पन्न प्रत्येक वर्ग में कोड जोड़ने की आवश्यकता होगी, न? वैसे भी, एक आगंतुक को लागू करने के निश्चित रूप से अन्य तरीके हैं, लेकिन यह एक अच्छा उदाहरण है जहां गतिशील जानवर उपयोगी हैं। –

+3

ठीक है, यही कारण है कि 'स्वीकार' आमतौर पर प्रत्येक व्युत्पन्न 'एलिमेंट' वर्ग (या सीआरटीपी के साथ) में लागू किया जाता है। इस तरह 'इस' का प्रकार संकलन-समय पर जाना जाता है। –

0

Contract Programming and RTTI दिखाता है कि आप dynamic_cast का उपयोग कैसे कर सकते हैं ताकि ऑब्जेक्ट्स को विज्ञापन देने की अनुमति दी जा सके कि वे कौन से इंटरफेस लागू करते हैं। हमने इसे अपनी दुकान में एक अपारदर्शी मेटाबोजेक्ट सिस्टम को बदलने के लिए इस्तेमाल किया। अब हम ऑब्जेक्ट्स की कार्यक्षमता का स्पष्ट रूप से वर्णन कर सकते हैं, भले ही प्लेटफॉर्म 'बेक्ड' के बाद कई हफ्तों/महीनों के बाद ऑब्जेक्ट्स को नए मॉड्यूल द्वारा पेश किया गया हो (भले ही अनुबंधों को सामने की ओर तय करने की आवश्यकता हो)।

2

ऑपरेटर dynamic_cast गतिशील प्रेषण (आभासी काम करता है, आगंतुक पैटर्न, आदि) के रूप में ही समस्या का हल: यह आप एक वस्तु के क्रम प्रकार के आधार पर अलग-अलग कार्रवाई करने के लिए अनुमति देता है।

हालांकि, आपको हमेशा डायनामिक प्रेषण पसंद करना चाहिए, सिवाय इसके कि जब dynamic_cast की संख्या आपको कभी बढ़ने की आवश्यकता नहीं होगी।

ईजी। आपको कभी नहीं करना चाहिए:

if (auto v = dynamic_cast<Dog*>(animal)) { ... } 
else if (auto v = dynamic_cast<Cat*>(animal)) { ... } 
... 

रखरखाव और प्रदर्शन कारणों के लिए, लेकिन आप ऐसा कर सकते हैं।

for (MenuItem* item: items) 
{ 
    if (auto submenu = dynamic_cast<Submenu*>(item)) 
    { 
     auto items = submenu->items(); 
     draw(context, items, position); // Recursion 
     ... 
    } 

    else 
    { 
     item->draw_icon(); 
     item->setup_accelerator(); 
     ... 
    } 
} 

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

1

गतिशील_कास्ट वर्चुअल फ़ंक्शंस के विकल्प के रूप में का इरादा नहीं है।
गतिशील_कास्ट में एक गैर-मामूली प्रदर्शन ओवरहेड है (या इसलिए मुझे लगता है) क्योंकि पूरे वर्ग पदानुक्रम के माध्यम से चलना होगा।
गतिशील_कास्ट सी # के 'is' ऑपरेटर और अच्छे पुराने COM के QueryInterface के समान है।

अब तक मैं dynamic_cast में से एक असली उपयोग पाया है:
(*) आपके पास एकाधिक वंशानुक्रम और कलाकारों संकलक लगाने के लिए नीचे वर्ग पदानुक्रम ऊपर चलना और के लिए है का लक्ष्य का पता लगाने की लक्ष्य (या यदि आप पसंद करते हैं तो नीचे और ऊपर)। इसका मतलब है कि कास्ट का लक्ष्य समानांतर शाखा में है जहां कास्ट का स्रोत पदानुक्रम में है। मुझे लगता है कि इस तरह के कलाकारों को करने का कोई और तरीका नहीं है।

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

कर बातें की तरह:

if (v = dynamic_cast(...)){} else if (v = dynamic_cast(...)){} else if ... 

एक प्रदर्शन बर्बादी है।

0

गतिशील_कास्ट ऑपरेटर मेरे लिए बहुत उपयोगी है।

#include <vector> 
#include <iostream> 
using namespace std; 

class Subject; class Observer; class Event; 

class Event { public: virtual ~Event() {}; }; 
class Observer { public: virtual void onEvent(Subject& s, const Event& e) = 0; }; 
class Subject { 
    private: 
     vector<Observer*> m_obs; 
    public: 
     void attach(Observer& obs) { m_obs.push_back(& obs); } 
    public: 
     void notifyEvent(const Event& evt) { 
      for (vector<Observer*>::iterator it = m_obs.begin(); it != m_obs.end(); it++) { 
       if (Observer* const obs = *it) { 
        obs->onEvent(*this, evt); 
       } 
      } 
     } 
}; 

// Define a model with events that contain data. 
class MyModel : public Subject { 
    public: 
     class Evt1 : public Event { public: int a; string s; }; 
     class Evt2 : public Event { public: float f; }; 
}; 
// Define a first service that processes both events with their data. 
class MyService1 : public Observer { 
    public: 
     virtual void onEvent(Subject& s, const Event& e) { 
      if (const MyModel::Evt1* const e1 = dynamic_cast<const MyModel::Evt1*>(& e)) { 
       cout << "Service1 - event Evt1 received: a = " << e1->a << ", s = " << e1->s << endl; 
      } 
      if (const MyModel::Evt2* const e2 = dynamic_cast<const MyModel::Evt2*>(& e)) { 
       cout << "Service1 - event Evt2 received: f = " << e2->f << endl; 
      } 
     } 
}; 
// Define a second service that only deals with the second event. 
class MyService2 : public Observer { 
    public: 
     virtual void onEvent(Subject& s, const Event& e) { 
      // Nothing to do with Evt1 in Service2 
      if (const MyModel::Evt2* const e2 = dynamic_cast<const MyModel::Evt2*>(& e)) { 
       cout << "Service2 - event Evt2 received: f = " << e2->f << endl; 
      } 
     } 
}; 

int main(void) { 
    MyModel m; MyService1 s1; MyService2 s2; 
    m.attach(s1); m.attach(s2); 

    MyModel::Evt1 e1; e1.a = 2; e1.s = "two"; m.notifyEvent(e1); 
    MyModel::Evt2 e2; e2.f = .2f; m.notifyEvent(e2); 
} 
संबंधित मुद्दे