2012-08-17 25 views
25

कोई सूचक नहीं है सिर्फ एक पता? या मुझे कुछ याद आ रहा है? किसी भी चर केकिसी फ़ंक्शन में पॉइंटर का आकार एक सूचक के आकार से किसी सदस्य फ़ंक्शन से अलग क्यों होता है?

  • संकेत दिए गए एक ही (मेरे मंच पर 8B)
  • कार्यों के लिए संकेत दिए गए एक ही आकार, चर के संकेत के रूप में (8 बी फिर से) कर रहे हैं:

    मैं संकेत के कई प्रकार के साथ परीक्षण किया

  • विभिन्न मापदंडों के साथ कार्यों के लिए संकेत - मेरे मंच पर 16B - अब भी वही (8 बी)

लेकिन संकेत सदस्य कार्यों के लिए बड़े हैं।

तीन बातें:

  1. क्यों सदस्य कार्यों की ओर इशारा बड़ा कर रहे हैं? उन्हें और जानकारी क्या चाहिए?
  2. जहाँ तक मुझे पता है, मानक को छोड़कर एक सूचक का आकार, बारे में कुछ नहीं है कि void* किसी भी सूचक प्रकार "में" में सक्षम होना चाहिए कहते हैं। दूसरे शब्दों में, किसी भी सूचक को void* पर डाला जा सकता है, है ना? यदि ऐसा है, तो sizeof(void*) क्यों 8 है, जबकि sizeof सदस्य फ़ंक्शन के लिए सूचक 16 है?
  3. क्या पॉइंटर्स के लिए कोई अन्य उदाहरण हैं, जो अलग-अलग आकार के हैं (मेरा मतलब है, मानक प्लेटफॉर्म, कुछ दुर्लभ और विशेष नहीं)?
+1

अनिवार्य रूप से, मानक सभी डेटा संकेत, समारोह संकेत और सदस्य समारोह संकेत की आवश्यकता नहीं है एक ही आकार की है। यदि आप जानना चाहते हैं कि उनके प्लेटफ़ॉर्म पर उनका आकार क्यों नहीं है, तो आपको अपने सी ++ कंपाइलर के रखरखावकर्ता से पूछना होगा। http://www.parashift.com/c++-faq/cant-cvt-memfnptr-to-voidptr.html http://www.parashift.com/c++-faq/cant-cvt-fnptr-to-voidptr.html – Cubic

+0

@ किरिलकिरोव कोई समस्या नहीं है। मैं हमेशा व्यंग्यात्मक नहीं हूं :) –

उत्तर

26

संपादित: तो मैंने देखा मैं अभी भी बाद में इस महीने पर वोट मिल रहा है, भले ही अपने मूल जवाब बुरा और गुमराह करने वाली है (मैं भी याद नहीं कर सकते कि मैं क्या समय में सोच रहा था, और यह बहुत समझ में नहीं आता है!) इसलिए मैंने सोचा कि मैं स्थिति की कोशिश करूँगा और स्पष्ट करूँगा, क्योंकि लोगों को अभी भी खोज के माध्यम से यहां जाना होगा।

सबसे सामान्य स्थिति में, आप कर सकते हैं की काफी थिंक

struct A { int i; int foo() { return i; } }; 
A a; a.foo(); 

रूप

struct A { int i; }; 
int A_foo(A* this) { return this->i; }; 
A a; A_foo(&a); 

(C तरह देखने के लिए, है ना? शुरू) तो तुम सूचक लगता होगा &A::foo सिर्फ होगा सामान्य फ़ंक्शन पॉइंटर के समान हो। लेकिन कुछ जटिलताओं हैं: एकाधिक विरासत, और आभासी कार्यों।

तो कल्पना हमने:

struct A {int a;}; 
struct B {int b;}; 
struct C : A, B {int c;}; 

यह इस तरह से बाहर रखा जा सकता है:

Multiple inheritance

आप, यदि आप एक A* के साथ वस्तु ध्यान खींचना चाहते हैं कर सकते हैं या एक C*, आप शुरुआत को इंगित करते हैं, लेकिन यदि आप B* के साथ इसे इंगित करना चाहते हैं तो आपको कहीं बीच में इंगित करना होगा।इसलिए यदि CB से कुछ सदस्य फ़ंक्शन प्राप्त करता है और आप इसे इंगित करना चाहते हैं, तो C* पर फ़ंक्शन को कॉल करें, इसे this पॉइंटर को घुमाने के लिए जानना आवश्यक है। उस जानकारी को कहीं भी संग्रहीत करने की आवश्यकता है। तो यह फंक्शन पॉइंटर के साथ लुप्त हो जाता है।

अब हर वर्ग virtual कार्य करता है कि के लिए, संकलक उनमें से एक सूची एक वर्चुअल टेबल कहा जाता है बनाता है। इसके बाद कक्षा में इस तालिका में एक अतिरिक्त सूचक जोड़ता है (vptr)। इसलिए इस वर्ग संरचना के लिए:

struct A 
{ 
    int a; 
    virtual void foo(){}; 
}; 
struct B : A 
{ 
    int b; 
    virtual void foo(){}; 
    virtual void bar(){}; 
}; 

संकलक इस तरह बना अंत हो सकता है: enter image description here

तो एक आभासी कार्य करने के लिए एक सदस्य समारोह सूचक वास्तव में आभासी तालिका में एक सूचकांक की जरूरत है। तो एक सदस्य फ़ंक्शन पॉइंटर को वास्तव में 1) संभवतः एक फ़ंक्शन पॉइंटर की आवश्यकता होती है, 2) संभवतः this पॉइंटर का समायोजन, और 3) संभवतः एक vtable अनुक्रमणिका। सुसंगत होने के लिए, प्रत्येक सदस्य फ़ंक्शन पॉइंटर को इन सभी में सक्षम होने की आवश्यकता होती है। तो 16 बाइट्स के लिए सूचकांक के लिए 4 बाइट समायोजन के लिए 4 बाइट्स के लिए 8 बाइट्स है।

मुझे विश्वास है कि यह ऐसा कुछ है जो वास्तव में कंपाइलर्स के बीच बहुत भिन्न होता है, और बहुत सारे संभावित अनुकूलन हैं। शायद कोई भी वास्तव में जिस तरीके से मैंने वर्णन किया है उसे लागू नहीं करता है।

बहुत विस्तार के लिए, this देखें ("सदस्य फ़ंक्शन पॉइंटर्स के कार्यान्वयन" तक स्क्रॉल करें)।

+0

मैंने मानक से पाठ डालने की स्वतंत्रता ली है जो मुझे लगता है कि आप ढूंढ रहे थे। मुझे उम्मीद है कि ठीक है। – jogojapan

+0

ध्यान दें कि जहां तक ​​मानक का संबंध है, 'int *' 'शून्य *' से छोटा हो सकता है; कुछ पुराने सी कार्यान्वयन के लिए, यह था। और जो कुछ 'शून्य *' के लिए गारंटीकृत है, वह गैर-सदस्य डेटा है: उदाहरण के लिए, गारंटी नहीं है कि सदस्य डेटा का सूचक 'शून्य * 'से बड़ा नहीं है (हालांकि मैं उचित कार्यान्वयन की कल्पना नहीं कर सकता यह होगा)। –

+1

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

6

मूल रूप से क्योंकि उन्हें पॉलिमॉर्फिक व्यवहार का समर्थन करने की आवश्यकता है। रेमंड चेन द्वारा एक अच्छा article देखें।

0

मुझे लगता है कि इसमें this पॉइंटर के साथ कुछ करना है ... यानी, प्रत्येक सदस्य फ़ंक्शन में कक्षा के लिए पॉइंटर भी होना चाहिए। पॉइंटर तब फ़ंक्शन को आकार में थोड़ा बड़ा बनाता है।

+3

नहीं, गलत अनुमान लगाया डिर्क के जवाब पर मेरी टिप्पणी देखें –

+0

यह जवाब भी गलत alomg है।। Dirk Holsopple's –

+0

:) मैंने सोचा, वैसे भी, लेकिन यह सच नहीं है। उदाहरण के लिए, क्योंकि इस तरह के पॉइंटर को शुरू करने के लिए किसी ऑब्जेक्ट से कोई लेना देना नहीं है, लेकिन केवल कक्षा घोषणा के साथ। इससे मुझे पता चला कि मैं हूं गलत। –

2

कुछ स्पष्टीकरण यहां पाया जा सकता है: The underlying representation of member function pointers

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

0

{this, T (*f)()} के रूप में सदस्य कार्यों के लिए संकेत का प्रतिनिधित्व करने के लिए मुख्य कारणों में से कुछ हैं:

  • यह T (*f)()

  • के रूप में सदस्य कार्यों के लिए संकेत को लागू करने से संकलक में सरल कार्यान्वयन है इसमें रनटाइम कोड पीढ़ी शामिल नहीं है और न ही अतिरिक्त बहीखाता

  • यह काफी अच्छा T (*f)()

  • की तुलना में कैसा प्रदर्शन कर रहे सदस्य कार्यों के लिए संकेत के आकार के लिए C++ प्रोग्रामर पर्याप्त मांग निष्पादन है जिस वास्तविक एक के दौरान sizeof(void*)

  • रनटाइम कोड पीढ़ी के बराबर होने के लिए नहीं है सी ++ कोड के लिए वर्जित वर्तमान में