एक क्या है vtable
?
यह जानने के लिए उपयोगी हो सकता है कि त्रुटि संदेश इसे ठीक करने की कोशिश करने से पहले क्या बात कर रहा है। मैं उच्च स्तर पर शुरू करूंगा, फिर कुछ और विवरणों पर काम करूंगा। इस तरह लोग vtables की अपनी समझ के साथ सहज होने के बाद आगे बढ़ सकते हैं। ... और अभी आगे जा रहे लोगों का एक झुंड चला जाता है। :) आसपास रहने वालों के लिए:
एक विटेबल मूल रूप से C ++ में बहुरूपता का सबसे आम कार्यान्वयन है । जब vtables का उपयोग किया जाता है, तो हर बहुरूपी वर्ग के कार्यक्रम में कहीं न कहीं एक व्यवहार्यता होती है; आप इसे कक्षा के एक (छिपे हुए) डेटा सदस्य के रूप में सोच सकते हैं । एक बहुरूपी वर्ग की प्रत्येक वस्तु अपने सबसे व्युत्पन्न वर्ग के लिए व्यवहार्य से जुड़ी होती है। इस एसोसिएशन की जाँच करके, कार्यक्रम अपने बहुरूपी जादू का काम कर सकता है। महत्वपूर्ण चेतावनी: एक व्यवहार्य एक कार्यान्वयन विस्तार है। यह C ++ मानक द्वारा अनिवार्य नहीं है, भले ही अधिकांश (सभी?) C ++ कंपाइलर पॉलीमॉर्फिक व्यवहार को लागू करने के लिए vtables का उपयोग करते हैं। जो विवरण मैं प्रस्तुत कर रहा हूं, वे या तो विशिष्ट हैं या उचित दृष्टिकोण हैं। कंपाइलरों को इससे भटकने दिया जाता है!static
प्रत्येक पॉलीमॉर्फिक ऑब्जेक्ट में ऑब्जेक्ट के सबसे व्युत्पन्न वर्ग (संभवतः अधिक जटिल मामलों में, एकाधिक पॉइंटर्स) के लिए व्यवहार्य के लिए एक (छिपा हुआ) सूचक होता है। पॉइंटर को देखकर, प्रोग्राम बता सकता है कि एक वस्तु का "वास्तविक" प्रकार क्या है (निर्माण के दौरान छोड़कर, लेकिन चलो उस विशेष को छोड़ दें)। उदाहरण के लिए, यदि किसी प्रकार की वस्तु A
वस्तुनिष्ठ की ओर इशारा नहीं करती है A
, तो वह वस्तु वास्तव में किसी वस्तु से उप-वस्तु है A
।
" Vtable " नाम " v irtual फंक्शन टेबल " से आता है । यह एक तालिका है जो पॉइंटर्स टू (वर्चुअल) फ़ंक्शन को स्टोर करती है। एक कंपाइलर अपने कन्वेंशन को चुनता है कि टेबल कैसे रखी जाए; एक साधारण तरीका यह है कि वर्चुअल फ़ंक्शंस से गुज़रना है ताकि उन्हें कक्षा की परिभाषाओं में घोषित किया जा सके। जब एक वर्चुअल फ़ंक्शन को कॉल किया जाता है, तो प्रोग्राम ऑब्जेक्ट के पॉइंटर को एक विटेबल पर फॉलो करता है, वांछित फ़ंक्शन के साथ जुड़े एंट्री पर जाता है, फिर सही फ़ंक्शन को इनवॉइस करने के लिए संग्रहीत फ़ंक्शन पॉइंटर का उपयोग करता है। इस काम को करने के लिए कई तरकीबें हैं, लेकिन मैं यहाँ नहीं जाऊँगा।
कहाँ / कब vtable
उत्पन्न होता है?
संकलक द्वारा एक विएबेट स्वचालित रूप से उत्पन्न होता है (कभी-कभी "उत्सर्जित" कहा जाता है)। एक संकलक हर अनुवाद इकाई में एक व्यवहार्य का उत्सर्जन कर सकता है जो एक बहुरूपी वर्ग परिभाषा देखता है, लेकिन यह आमतौर पर अनावश्यक टोकिल होगा। एक विकल्प ( जीसीसी द्वारा उपयोग किया जाता है , और शायद दूसरों के द्वारा) एक एकल अनुवाद इकाई को चुनना है जिसमें व्यवहार्य जगह है, इसी तरह आप एक एकल स्रोत फ़ाइल को चुनेंगे जिसमें क्लास के स्थैतिक डेटा सदस्यों को रखा जा सके। यदि यह चयन प्रक्रिया किसी भी अनुवाद इकाइयों को लेने में विफल रहती है, तो व्यवहार्य एक अपरिभाषित संदर्भ बन जाता है। इसलिए त्रुटि, जिसका संदेश विशेष रूप से स्पष्ट नहीं है।
इसी तरह, यदि चयन प्रक्रिया एक अनुवाद इकाई को चुनती है, लेकिन वह ऑब्जेक्ट फ़ाइल लिंकर को प्रदान नहीं की जाती है, तो विटेब एक अपरिभाषित संदर्भ बन जाता है। दुर्भाग्य से, त्रुटि संदेश इस मामले में उस मामले की तुलना में कम स्पष्ट हो सकता है जहां चयन प्रक्रिया विफल रही। (उत्तरदाताओं के लिए धन्यवाद जिन्होंने इस संभावना का उल्लेख किया है। मैं शायद इसे अन्यथा भूल गया होता।)
जीसीसी द्वारा उपयोग की जाने वाली चयन प्रक्रिया समझ में आती है यदि हम प्रत्येक वर्ग के लिए एक (एकल) स्रोत फ़ाइल को समर्पित करने की परंपरा से शुरू करते हैं जिसे इसके कार्यान्वयन के लिए एक की आवश्यकता होती है। उस स्रोत फ़ाइल को संकलित करते समय व्यवहार्यता का उत्सर्जन करना अच्छा होगा। चलो कि हमारे लक्ष्य को बुलाओ। हालाँकि, चयन प्रक्रिया को काम करने की आवश्यकता है भले ही इस परंपरा का पालन न किया जाए। इसलिए पूरी कक्षा के कार्यान्वयन की तलाश करने के बजाय, आइए कक्षा के किसी विशिष्ट सदस्य के कार्यान्वयन की तलाश करें। यदि परंपरा का पालन किया जाता है - और यदि वह सदस्य वास्तव में कार्यान्वित किया जाता है - तो यह लक्ष्य को प्राप्त करता है।
Gcc द्वारा चयनित सदस्य (और संभवतः अन्य संकलक द्वारा) पहला गैर-इनलाइन वर्चुअल फ़ंक्शन है जो शुद्ध वर्चुअल नहीं है। यदि आप उस भीड़ का हिस्सा हैं जो अन्य सदस्य कार्यों से पहले कंस्ट्रक्टर और विध्वंसक घोषित करता है, तो उस विध्वंसक के पास चुने जाने की अच्छी संभावना है। (आपने विध्वंसक को आभासी बनाने के लिए याद किया, ठीक है?) कुछ अपवाद हैं; मुझे उम्मीद है कि सबसे आम अपवाद तब होते हैं जब विध्वंसक के लिए एक इनलाइन परिभाषा प्रदान की जाती है और जब डिफ़ॉल्ट विध्वंसक का अनुरोध किया जाता है (" = default
" का उपयोग करके )।
अचरज यह देख सकता है कि एक बहुरूपी वर्ग को अपने सभी आभासी कार्यों के लिए इनलाइन परिभाषाएँ प्रदान करने की अनुमति है। क्या चयन प्रक्रिया विफल होने का कारण नहीं है? यह पुराने कंपाइलरों में होता है। मैंने पढ़ा है कि नवीनतम संकलक ने इस स्थिति को संबोधित किया है, लेकिन मुझे प्रासंगिक संस्करण संख्याएं नहीं पता हैं। मैं इसे देखने की कोशिश कर सकता था, लेकिन इसके आस-पास या तो कोड करना आसान है या शिकायत करने वाले का इंतजार करना।
सारांश में, "अपरिहार्य संदर्भ के लिए व्यवहार्य" त्रुटि के तीन प्रमुख कारण हैं:
- एक सदस्य फ़ंक्शन इसकी परिभाषा याद कर रहा है।
- ऑब्जेक्ट फ़ाइल लिंक नहीं की जा रही है।
- सभी वर्चुअल फ़ंक्शंस में इनलाइन परिभाषाएँ होती हैं।
ये कारण स्वयं अपने आप में त्रुटि का कारण बनने के लिए अपर्याप्त हैं। बल्कि, ये वही हैं जिन्हें आप त्रुटि को हल करने के लिए संबोधित करेंगे। उम्मीद न करें कि इन स्थितियों में से एक को जानबूझकर पैदा करना निश्चित रूप से इस त्रुटि का उत्पादन करेगा; अन्य आवश्यकताएं हैं। उम्मीद करें कि इन स्थितियों को हल करने से यह त्रुटि हल हो जाएगी।
(ठीक है, यह प्रश्न पूछे जाने पर संख्या 3 पर्याप्त हो सकती है।)
त्रुटि कैसे ठीक करें?
आपका स्वागत है लोगों को छोड़ आगे बढ़ना! :)
- अपनी कक्षा की परिभाषा देखें। पहला गैर-इनलाइन वर्चुअल फ़ंक्शन खोजें जो शुद्ध वर्चुअल नहीं है ("
= 0
") और जिसकी परिभाषा आप प्रदान करते हैं (" = default
") नहीं।
- यदि ऐसा कोई फ़ंक्शन नहीं है, तो अपनी कक्षा को संशोधित करने का प्रयास करें ताकि एक हो। (त्रुटि संभवतः हल हो गई है।)
- एक चेतावनी के लिए फिलिप थॉमस द्वारा उत्तर भी देखें ।
- उस फ़ंक्शन के लिए परिभाषा खोजें। यदि यह गायब है, तो इसे जोड़ें! (त्रुटि संभवतः हल हो गई है।)
- अपने लिंक कमांड की जाँच करें। यदि यह उस फ़ंक्शन की परिभाषा के साथ ऑब्जेक्ट फ़ाइल का उल्लेख नहीं करता है, तो इसे ठीक करें! (त्रुटि संभवतः हल हो गई है।)
- प्रत्येक वर्चुअल फ़ंक्शन के लिए चरण 2 और 3 को दोहराएं, फिर प्रत्येक गैर-वर्चुअल फ़ंक्शन के लिए, जब तक कि त्रुटि हल न हो जाए। यदि आप अभी भी अटके हुए हैं, तो प्रत्येक स्थिर डेटा सदस्य के लिए दोहराएं।
उदाहरण
क्या करना है, इसका विवरण अलग-अलग हो सकता है, और कभी-कभी अलग-अलग प्रश्नों में विभाजित होता है (जैसे कि एक अपरिभाषित संदर्भ / अनसुलझे बाहरी प्रतीक त्रुटि क्या है और मैं इसे कैसे ठीक करूं? )। मैं, हालांकि, एक विशिष्ट मामले में क्या करना है, इसका एक उदाहरण प्रदान कर सकता हूं जो नए प्रोग्रामर को परेशान कर सकता है।
चरण 1 में आपकी कक्षा को संशोधित करने का उल्लेख है ताकि इसमें एक निश्चित प्रकार का कार्य हो। यदि उस फ़ंक्शन का विवरण आपके सिर पर चला गया है, तो आप उस स्थिति में हो सकते हैं जिसे मैं संबोधित करना चाहता हूं। ध्यान रखें कि यह लक्ष्य को पूरा करने का एक तरीका है; यह एकमात्र तरीका नहीं है, और आपकी विशिष्ट स्थिति में आसानी से बेहतर तरीके हो सकते हैं। चलो अपनी कक्षा को बुलाओ A
। क्या आपका विध्वंसक (आपकी कक्षा की परिभाषा में) घोषित किया गया है
virtual ~A() = default;
या
virtual ~A() {}
? यदि हां, तो दो चरण आपके विध्वंसक को उस प्रकार के फ़ंक्शन में बदल देंगे जो हम चाहते हैं। सबसे पहले, उस लाइन को बदल दें
virtual ~A();
दूसरा, निम्न लाइन को एक स्रोत फ़ाइल में रखें जो आपकी परियोजना का हिस्सा है (अधिमानतः वर्ग कार्यान्वयन के साथ फ़ाइल, यदि आपके पास एक है):
A::~A() {}
यह आपके (आभासी) विध्वंसक गैर-इनलाइन बनाता है और संकलक द्वारा उत्पन्न नहीं होता है। (अपनी कोड स्वरूपण शैली से बेहतर मिलान करने के लिए चीजों को संशोधित करने के लिए स्वतंत्र महसूस करें, जैसे कि फ़ंक्शन परिभाषा में हेडर टिप्पणी जोड़ना।)