2011-01-06 51 views
31

मैं एक सी ++ प्रोग्रामिंग क्लास पढ़ता हूं और मैंने त्रुटियों के पर्याप्त वर्गों को देखा है कि मुझे सामान्य सी ++ बग का निदान करने के लिए अच्छी भावना है। हालांकि, एक बड़ी प्रकार की त्रुटि है जिसके लिए मेरा अंतर्ज्ञान विशेष रूप से अच्छा नहीं है: क्या प्रोग्रामिंग त्रुटियां शुद्ध आभासी कार्यों को कॉल करती हैं? मैंने देखा है कि सबसे आम त्रुटि यह कारण है कि यह एक बेस क्लास कन्स्ट्रक्टर या विनाशक से वर्चुअल फ़ंक्शन को कॉल कर रहा है। क्या कोई अन्य व्यक्ति है जो मुझे डीबग छात्र कोड की सहायता करते समय अवगत होना चाहिए?सी ++ में शुद्ध आभासी फ़ंक्शन कॉल का कारण क्या हो सकता है?

+0

अन्य लोग इसे बेस क्लास के कुछ सदस्य कार्यों से बुला सकते हैं, और क्या हो सकता है? लेकिन फिर यह कोई त्रुटि नहीं है! : | – Nawaz

+0

यह वास्तव में मेरा प्रश्न है। :-) शुद्ध वर्चुअल कॉल को ट्रिगर करने का कोई और तरीका नहीं हो सकता है, और मेरा मुख्य प्रश्न यह है कि कोई है या नहीं। – templatetypedef

उत्तर

27
बाहर ध्यान केंद्रित किया जा सकता है अब

"मैंने देखा है कि सबसे आम त्रुटि यह कारण है कि यह बेस क्लास कन्स्ट्रक्टर या विनाशक से वर्चुअल फ़ंक्शन को कॉल कर रहा है।"

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

यदि आप आंशिक रूप से निर्मित ऑब्जेक्ट (जैसे एसिंक या थ्रेडेड ऑपरेशंस के कारण दौड़ की स्थिति में) के सूचक का पालन करते हैं तो ऐसा हो सकता है।

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

विनाश के दौरान, व्युत्पन्न कक्षाओं को नष्ट कर दिया जाना चाहिए क्योंकि व्युत्पन्न कक्षाएं नष्ट हो जाती हैं, इसलिए शुद्ध आभासी कार्यान्वयन फिर से लागू किया जा सकता है।

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

+0

वास्तव में, कन्स्ट्रक्टर और विनाशकों में वर्चुअल कॉल अक्षम हैं - [यहां समझाया गया है) (https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors) - इसलिए, कॉल वर्चुअल और लिंकर नहीं हैं त्रुटि होती है। कोई भी गैर वर्चुअल प्रेषण लिंकर त्रुटि का कारण बन जाएगा। लेकिन वर्चुअल टेबल बस गलत होने पर मैं आंशिक रूप से निर्मित (या दूषित) ऑब्जेक्ट्स के लिए सहमत हूं। हालांकि, इन स्थितियों में गतिशील चाल की आवश्यकता होती है जो संकलक द्वारा नहीं देखा जाता है (जैसा कि उल्लेख किया गया है, reinterpret_cast, आदि)। – uvsmtid

+0

@uvsmtid: "वास्तव में" का तात्पर्य है कि आपने कुछ ग़लत दावे को सही कर दिया है, लेकिन मैंने रचनाकारों और विनाशकों का उल्लेख नहीं किया, केवल * निर्माण * और * विनाश *। निर्माणकर्ता की प्रारंभिक सूची और शरीर संसाधित होने से पहले निर्माण में बेस क्लास और गैर स्थैतिक डेटा सदस्य प्रारंभिक होते हैं; विनाश के दौरान रिवर्स में यह काफी समान है। शुद्ध आभासी कार्यों के लिए प्रेषण सचित्र [यहां] (http://coliru.stacked-crooked.com/a/29b4fa463e39e277) के बिना हो सकता है, थ्रेडिंग, async, 'reinterpret_cast' आदि के बिना .. –

+0

अलग-अलग, आपके द्वारा प्रदान किए गए लिंक से या अन्यथा - मैं कल्पना नहीं कर सकता कि लिंकर त्रुटियां कैसे प्रकट होती हैं। क्या आप कृपया मुझे कुछ कोड दिखा सकते हैं? शायद उपरोक्त टिप्पणी में मैंने जो कॉलरू साइट लिंक की है, ideone.com या जो कुछ भी आप पसंद करते हैं .... चीयर्स –

8

यहां कुछ ऐसे मामले हैं जिनमें शुद्ध वर्चुअल कॉल हो सकता है।

  1. एक dangling सूचक का उपयोग करना - सूचक एक वैध वस्तु इतनी वर्चुअल टेबल यह की ओर इशारा की नहीं है बस यादृच्छिक स्मृति जो शून्य
  2. बुरा डाली शामिल गलत करने के लिए एक static_cast का उपयोग कर सकता है टाइप (या सी-स्टाइल कास्ट) उस ऑब्जेक्ट का कारण बन सकता है जिस पर आप अपनी आभासी तालिका में सही तरीके से नहीं हैं (इस मामले में कम से कम यह वास्तव में पिछले विकल्प के विपरीत वर्चुअल टेबल है)।
  3. DLL उतार दिया गया है - वस्तु आप के लिए एक साझा वस्तु फ़ाइल (DLL, इसलिए, sl) जो फिर से उतार दिया दिया गया है में बनाया गया था पर रोक रखी है स्मृति
+0

तीनों को पाने का एक तरीका नहीं है? – GManNickG

+2

शुद्धकॉल हैंडलर अक्सर एक विशेष कार्य होता है, न कि पूर्ण। – ephemient

+0

@GMan, काफी हां, शायद यह एक अलग बुलेट बिंदु के लायक नहीं है लेकिन सामान्य मामले – Motti

0

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

std::vector <DerivedClass> objContainer; 
if (!objContainer.empty()) 
    const BaseClass& objRef = objContainer.front(); 
// Do some processing using objRef and then you erase the first 
// element of objContainer 
objContainer.erase(objContainer.begin()); 
const std::string& name = objRef.name(); 
// -> (name() is a pure virtual function in base class, 
// which has been implemented in DerivedClass). 

इस बिंदु पर objContainer [0] में संग्रहीत वस्तु मौजूद नहीं है। जब वर्चुअल तालिका अनुक्रमित होती है, तो कोई मान्य स्मृति स्थान नहीं मिलता है। इसलिए, एक रन टाइम त्रुटि जारी की जाती है "शुद्ध वर्चुअल फ़ंक्शन" कहा जाता है।

+0

हां मैंने कोशिश की और "शुद्ध आभासी विधि" नामक त्रुटि मिली। आप क्या त्रुटि देख रहे हैं? – cppcoder

+0

उदाहरण अपूर्ण है, इसलिए मैंने इसे अभी तक पुनर्निर्माण नहीं किया है। लेकिन जैसा कि मुझे लगता है, आप एक खतरनाक सूचक के लिए वर्चुअल फ़ंक्शन को कॉल कर रहे हैं, मैं जल्द ही इसे आजमाउंगा। बीटीडब्ल्यू: यह अनिर्धारित व्यवहार है और इसलिए यह एक अलग तरीके से दुर्घटनाग्रस्त हो सकता है। – Wolf

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