2009-07-06 14 views
22

निम्नलिखित सी ++ कोड पर विचार करें:आभासी सदस्य कार्यों के लिए पॉइंटर्स। यह कैसे काम करता है?

class A 
{ 
public: 
     virtual void f()=0; 
}; 


int main() 
{ 
    void (A::*f)()=&A::f; 
} 

मुझे लगता है कि करने के लिए है चाहते हैं, तो मैं कहता हूँ चाहते हैं कि & एक :: इस संदर्भ में च मतलब होगा "च के एक के कार्यान्वयन का पता()", चूंकि नियमित सदस्य कार्यों और आभासी सदस्य कार्यों के लिए पॉइंटर्स के बीच कोई स्पष्ट पृथक्करण नहीं होता है। और चूंकि ए f() को लागू नहीं करता है, यह एक संकलित त्रुटि होगी। हालांकि, यह नहीं है।

और न केवल वह। निम्नलिखित कोड:

void (A::*f)()=&A::f; 
A *a=new B;   // B is a subclass of A, which implements f() 
(a->*f)(); 

वास्तव में बी :: एफ कॉल करेगा।

यह कैसे होता है?

+0

क्योंकि संकलक ऐसा करता है! यदि एक सामान्य विधि को कॉल करना वर्चुअल विधि को कॉल करने से अलग नहीं है, तो आपको लगता है कि विधि पॉइंटर्स का उपयोग करते समय कोड अलग क्यों होता है। आपको क्या लगता है कि संकलक सामान्य विधि (आभासी और ordingary) में अनुवाद कर रहा है? –

उत्तर

9

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

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

संक्षिप्त उत्तर है कि यह संकलक पर निर्भर करता है, लेकिन एक संभावना है कि सदस्य समारोह सूचक एक struct एक "thunk" समारोह जो आभासी कॉल करता है के लिए सूचक युक्त के रूप में कार्यान्वित किया जाता है है।

+0

नमस्कार, आपने बस थंक के बारे में बताया। क्या वहां कुछ अच्छा लेख है जो थक गया है? – anand

+0

"शब्द थंक कम-स्तर कोड के एक टुकड़े को संदर्भित करता है, आमतौर पर मशीन से उत्पन्न, जो किसी विशेष सॉफ़्टवेयर सिस्टम के कुछ विवरण लागू करता है।", Http://en.wikipedia.org/wiki/Thunk से। वह पृष्ठ कई प्रकार के थंक्स पर चर्चा करता है, हालांकि यह विशेष नहीं है। –

22

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

struct A { virtual void f() { } virtual void g() { } }; 
int main() { 
    union insp { 
    void (A::*pf)(); 
    ptrdiff_t pd[2]; 
    }; 
    insp p[] = { { &A::f }, { &A::g } }; 
    std::cout << p[0].pd[0] << " " 
      << p[1].pd[0] << std::endl; 
} 

कि कार्यक्रम आउटपुट 1 5 - उन दो कार्यों की आभासी तालिका प्रविष्टियों की बाइट ऑफसेट। यह इटेनियम सी ++ एबीआई, which specifies that का पालन करता है।

+0

मुझे लगता है कि मेरे प्रश्न का उत्तर सी ++ स्टैंडराइज्ड नहीं है। हालांकि, vtables हैं, फिर भी मुझे किसी भी कंपाइलर के बारे में पता नहीं है जो वर्चुअल फ़ंक्शंस के लिए तंत्र के रूप में vtables का उपयोग नहीं करता है, इसलिए मुझे लगता है कि इसके लिए * मानक * तंत्र भी है। आपका जवाब केवल मुझे और उलझन में डाल देता है।यदि कंपाइलर ए और सदस्य फ़ंक्शंस में पॉइंटर्स में 1 और 5 स्टोर करता है, तो यह कैसे बता सकता है कि यह एक vtable अनुक्रमणिका या वास्तविक पता है या नहीं? (ध्यान दें कि नियमित और आभासी सदस्य कार्यों के लिए पॉइंटर्स के बीच कोई अंतर नहीं है) –

+0

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

+0

धन्यवाद, यह तार्किक लगता है। हालांकि, सी ++ कार्यान्वयन के लिए कुछ हद तक अक्षम ... वीसी पर कोड की जांच की, और परिणाम पूरी तरह से अलग हैं। आउटपुट 'c01380 c01390' है, जो कुछ के पते की तरह लगता है। –

1

मैं पूरी तरह से निश्चित नहीं हूं, लेकिन मुझे लगता है कि यह केवल नियमित बहुलक व्यवहार है। मुझे लगता है कि &A::f वास्तव में वर्ग के vtable में फ़ंक्शन पॉइंटर का पता है, और यही कारण है कि आपको संकलक त्रुटि नहीं मिल रही है। Vtable में स्थान अभी भी आवंटित किया गया है, और यह वह स्थान है जहां आप वास्तव में वापस आ रहे हैं।

यह समझ में आता है क्योंकि व्युत्पन्न कक्षाएं इन मानों को पॉइंटर्स के साथ अपने कार्यों में अनिवार्य रूप से ओवरराइट करती हैं। यही कारण है कि (a->*f)() आपके दूसरे उदाहरण में काम करता है - f व्युत्पन्न वर्ग में लागू किए गए vtable का संदर्भ दे रहा है।

+1

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

+0

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

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