2009-10-04 13 views
8

में भ्रम निम्नलिखित प्रोग्राम पर विचार करें।आउटपुट

#include<iostream> 
using namespace std; 

class base 
{ 

    public: 
    int _bval; 

    base():_bval(0){} 
}; 

class derived:public base 
{ 

    public: 
    int _dval; 

    derived():base(),_dval(1){} 
}; 

int main() 
{  

    derived d[5]; 
    base *p; 
    p=d; 
    for(int i=0;i<5;++i,++p) 
     cout<<p->_bval; 

} 

उपरोक्त कार्यक्रम के उत्पादन में 01010.
मैंने सोचा था कि उत्पादन 00000 होगा क्योंकि _bval का मूल्य आधार वर्ग निर्माता द्वारा 0 (हर बार) के लिए शुरू किया गया था।

लेकिन आउटपुट 00000 से अलग क्यों है?
मुझे क्या याद आ रही है?

उत्तर

7

संक्षिप्त उत्तर: सी ++ में, मानों के सरणी कभी भी पॉलिमॉर्फिक नहीं होते हैं, भले ही उनकी सामग्री है, और इसका इलाज नहीं किया जा सकता है। यही है, आप derived ad[N] का इलाज नहीं कर सकते जैसे कि यह base ab[N] था।

लंबा उत्तर: इसका कारण सी के सूचक अंकगणितीय में गहराई से दफनाया गया है। यदि आपके पास int* pi है और इसे ++pi बढ़ाएं, तो यह केवल अगले मेमोरी पते में वृद्धि नहीं करेगा। यदि ऐसा होता है, तो यह अगले int पर इंगित नहीं करेगा क्योंकि यह अगले पते पर शुरू नहीं होता है। तो इसके बजाय सूचकांक में sizeof(int) बाइट जोड़े गए हैं। (एक ठोस उदाहरण मदद कर सकता है:। 8bit char प्रकार के साथ आर्किटेक्चर पर - char किया जा रहा है, परिभाषा के द्वारा क्या सी और सी वास्तुकला की बाइट आकार पर विचार ++ - और 32 बिट int प्रकार, int 4 बाइट्स की आकार की है इस प्रकार, ++pi 4 में जोड़ देगा पॉइंटर्स पता, ताकि यह अगले int पर इंगित करे।) एक ही अंकगणित अन्य सभी सूचक संचालन पर लागू होता है। तो, उदाहरण के लिए, int* pi2=pi+1 साथ, pi2sizeof(int) बाइट्स pi के पीछे, इंगित करेगा हालांकि pi2-pi 1.

निकलेगा तो, यह मानते हुए आप अंतिम अनुच्छेद समझ में आया, की सरणियों के लिए वापस चलते हैं। यदि आपके पास derived ad[N] है, तो ad[1] का पता ad[0] के पते से अधिक बाइट्स है।(यह समस्या को और जटिल नहीं करने के लिए संरेखण को अनदेखा कर रहा है।) हालांकि, यदि आपके पास base* pbad[0] पर इशारा करते हैं, तो इसे बढ़ाने से यह पहले तत्व के पते के पीछे sizeof(base) को इंगित करेगा - यदि, यदि (जैसा मामला है आपका उदाहरण) sizeof(base) < sizeof(derived), ad[1] का पता है, लेकिन कहीं ad[0] के बीच में है।

derived d[5]; 
derived* begin = d; 
const derived* end = d + sizeof(d)/sizeof(d[0]); // points one beyond the last element 
while(begin != end) 
{ 
    base* pb = begin; 
    cout<< pb->_bval; 
    ++begin; 
} 
:

केवल एक चीज आप सरणी सामग्री के इलाज के लिए के रूप में अगर यह सब आधार वर्ग था कर सकते हैं, एक derived* का उपयोग कर सरणी पर पुनरावृति और पाश भीतर base*को यह सूचक कास्ट करने के लिए है

(ध्यान दें कि मैंने आपके कोड को C++ 'idiomatic start/end iterators का उपयोग करने के लिए भी बदल दिया है।)

+0

सटीक होने के लिए धन्यवाद। –

11

p[i] आपको p के बाद sizeof(base) * i बाइट्स पर मूल्य देता है। तो p[1] आपको d का दूसरा तत्व नहीं देगा, यह आपको पहले तत्व का दूसरा भाग देगा।

दूसरे शब्दों में: यदि आप व्युत्पन्न वर्ग की एक सरणी पर पुनरावृत्ति करने के लिए बेस क्लास में पॉइंटर का उपयोग करते हैं, तो आपको गलत परिणाम मिलेंगे यदि व्युत्पन्न वर्ग बेस क्लास से बड़ा आकार है क्योंकि यह फिर से चालू होगा sizeof(baseclass) बाइट्स के चरण।

+0

यदि आप बदलते हैं, तो हम सही हैं, अगर हम' i <10' हम और अधिक होंगे '01' :) – IProblemFactory

+0

लेकिन कैसे _bval का मान '1' होना चाहिए? –

+0

'बावल' कक्षा बी का एकमात्र सदस्य चर है। इसलिए 'बेस' ऑब्जेक्ट का 'बावल' प्राप्त करने के लिए आप 'address_of_the_base_object + 0' पर मान लेते हैं। तो जब पी 'व्युत्पन्न' ऑब्जेक्ट के दूसरे भाग में इंगित कर रहा है, तो यह 'bval' के पते की गणना करने के लिए' p + 0' करेगा, जो इस मामले में वास्तव में 'dval' के पते को वापस कर देगा क्योंकि' p 'वास्तव में' आधार 'ऑब्जेक्ट पर इंगित नहीं कर रहा है। – sepp2k

3

डी सरणी के मेमोरी लेआउट के बारे में सोचें।

डी> 0101010101

कहाँ 01 की प्रत्येक जोड़ी के एक व्युत्पन्न वस्तु के अनुरूप हैं।

अब यह करने के लिए पी बिंदु हैं:

p-> 0101010101

के बाद से आधार ऑब्जेक्ट का आकार एक पूर्णांक का है। उस मेमोरी सेगमेंट को 10 बेस ऑब्जेक्ट माना जाता है: पहला वाला _bval 0 वाला, दूसरा वाला _bval 1, ... आदि।

+0

साफ! मेमोरी लेआउट दिखा रहा है और बेस पॉइंटर अर्थात् समझाते हुए बहुत स्पष्ट है। +1 :) – legends2k

1

क्या sepp2k ने कहा, आपने व्युत्पन्न वर्ग निर्माता में _bval प्रारंभ नहीं किया था। आपको base कन्स्ट्रक्टर का उपयोग करके इसे प्रारंभ करना चाहिए।