2009-06-04 34 views
5

एक बहुत ही बुनियादी सवाल है, लेकिन फिर भी, सी ++ गुरुओं से वहां सुनना अच्छा होगा।पैरामीटर परिभाषाओं के बीच अंतर (प्रकार और नाम), और (प्रकार * नाम) के बीच अंतर क्या हैं?

सी ++ में संदर्भ संदर्भ पैरामीटर घोषित करने के दो समान तरीके हैं।

1) "तारांकन" का उपयोग करना:

void DoOne(std::wstring* iData); 

2) "एम्परसेंड" का उपयोग करना:

void DoTwo(std::wstring& iData); 

प्रत्येक विधि के निहितार्थ क्या हैं? क्या किसी भी मामले में कोई गॉचा है?

बोनस # 1: # 1 और # 2 में विधि को कॉल करने का औपचारिक तरीका क्या होगा? क्या वे दोनों "संदर्भ द्वारा" कहा जाता है?

बोनस # 2: std :: wstring जानबूझकर उपयोग किया जाता है। प्रत्येक मामले में मानक पुस्तकालय कक्षाओं के प्रति क्या प्रभाव होगा?

+1

संदर्भ/सूचक तर्क के बारे में बहुत कुछ कहा गया है। यहां भी एक नज़र डालें: http://stackoverflow.com/questions/57483/difference-between-pointer-variable-and-reference-variable-in-c। – xtofl

उत्तर

5

# 1 एक पॉइंटर पैरामीटर ('पॉइंटर को पास करना') का उपयोग करता है, # 2 एक संदर्भ पैरामीटर ('संदर्भ द्वारा गुजर रहा है') का उपयोग करता है। वे बहुत समान हैं, लेकिन यह है कि फोन करने कोड दो मामलों में अलग दिखता है पर ध्यान दें:

std::wstring s; 

DoOne(&s); // pass a pointer to s 
DoTwo(s); // pass s by reference 

कुछ लोग, # 1 पसंद करते हैं एक सम्मेलन सूचक गुजर इंगित करता है कि समारोह भी रों का मूल्य बदल सकता है (का उपयोग करते हुए हालांकि या तो काम कर सकता है)। अन्य लोगों (स्वयं शामिल) # 2 पसंद करते हैं, क्योंकि संदर्भ से गुजरने से न्यूल को पारित करने की अनुमति नहीं मिलती है।

const पॉइंटर या संदर्भ से गुज़रने पर एक और महत्वपूर्ण अंतर है। एक अस्थायी चर केवल एक स्थिरांक संदर्भ पैरामीटर के लिए पारित किया जा सकता है:

void ByConstPointer(const std::wstring&); 
void ByConstReference(const std::wstring*); 

void test() 
{ 
    ByConstPointer(&std::wstring(L"Hello")); // error: cannot take address of temporary 
    ByConstReference(std::wstring(L"Hello")); // fine 
} 
+1

+1! –

1

उदाहरण लिखते समय, मैं अपने उत्तर के साथ आया। नीचे के अलावा कुछ भी?

उनमें से प्रत्येक का परिणाम काफी समान है: स्मृति में किसी ऑब्जेक्ट का संदर्भ विधि के दायरे में समाप्त होता है। उनमें से किसी के लिए कोई सख्त स्मृति आवश्यकता नहीं प्रतीत होती है। वस्तु या तो ढेर या ढेर में हो सकती है।

ढेर के मामले में तरीकों में से प्रत्येक इस तरह कहा जा सकता है:

{ 
    std::wstring data; 
    DoOne(&data); 
    DoTwo(data); 
} 

फिर भी, जब ढेर की बात आती है, दूसरा दृष्टिकोण की आवश्यकता होगी कि वस्तु विधि कॉल करने से पहले मौजूद होना चाहिए। यदि ऑब्जेक्ट मौजूद नहीं है, तो कॉलर अपवाद का कारण बनता है, कैली नहीं।

{ 
    std::wstring* pData = new std::wstring(); 
    DoOne(pData); 
    DoTwo(*pData); 
} 

ऊपर में, यदि बाहर के स्मृति स्थिति उत्पन्न होती है और pdata शून्य समाप्त होता है, दुर्घटना DoTwo से पहले हो जाएगा, लेकिन Doone शून्य निगल जाएगा और बाद में कुछ समय के क्रैश हो सकता है।

+0

+1 जोड़ने के लिए बहुत कुछ नहीं :) – ralphtheninja

+0

वहाँ है! औपचारिक रूप से प्रत्येक विधि को कैसे कहा जाता है? –

+0

DoOne पास-बाय-रेफरेंस है, DoTwo पास-बाय-पॉइंटर है। संदर्भ पूर्ण नहीं हो सकते हैं, यह संकलक द्वारा लागू किया जाता है। तो आपका कथन "यदि ऑब्जेक्ट मौजूद नहीं है, तो कॉलर अपवाद का कारण बनता है" गलत है। इसके अलावा, नया वापस नहीं लौटा सकता है। अगर यह विफल रहता है, तो यह एक अपवाद फेंकता है। तो आपको चिंता करने की ज़रूरत नहीं है। अधिकांश मामलों में पास-बाय-रेफरेंस को प्राथमिकता दी जाती है। – rlbond

0

मैं खुद को एक सी ++ गियर (मेरे सीवी पर छोड़कर) नहीं कहूंगा, लेकिन मैं कहूंगा; जब तक कि पॉइंटर के रूप में पैरामीटर को पारित करने का कोई उपयोग न हो (यानी फ़ंक्शन शून्य के लिए जांचना चाहता है), हमेशा एक संदर्भ का उपयोग करें।

यह ऑब्जेक्ट्स लौटने वाले कार्यों के लिए भी जाता है, एक पॉइंटर लौटने से किसी भी तरह कक्षा के उपयोगकर्ता को यह बताया जाता है कि यह शून्य हो सकता है।

0

DoOne में, iData को असाइन किया जा सकता है। यदि आप DoOne को कॉल करने के बाद इसका उपयोग करते हैं, तो एप्लिकेशन क्रैश हो जाएगा।

कुछ

void DoOne(std::wstring* iData) 
{ 
    //Use iData 
    delete iData; 
    iData = NULL; 
} 

और

तरह
{ 
    std::wstring* pData = new std::wstring(); 
    DoOne(pData); 
    pData->someFunction(); //Crash 
} 
+0

यह क्रैश हो जाएगा क्योंकि ऑब्जेक्ट हटा दिया गया था, न कि पॉइंटर को पूर्ण करने के लिए असाइन किया गया था। –

0

आपका जवाब पूरी तरह से गलत है जब आप कहते हैं:

उनमें से प्रत्येक का परिणाम काफी समान है: के लिए एक संदर्भ में एक ऑब्जेक्ट मेमोरी विधि स्कोप के भीतर समाप्त होता है।उनमें से किसी के लिए कोई सख्त स्मृति आवश्यकताएं प्रतीत नहीं होती हैं।

connsider:

void f(int * p1) { 
    int ** p2 = & p1; 
} 
यहाँ

p1 एक निश्चित "स्मृति आवश्यकता" है - यह मौजूद होना चाहिए और मैं इसका पता लेने के लिए सक्षम होना चाहिए।

void f(int & r) } 
    int * p = & r; 
} 

यहां आर के अपने अस्तित्व का कोई अस्तित्व नहीं है, यह केवल एक संदर्भ है। Whem मैं इसे पता लेता हूं, मैं उस चीज़ का पता ले रहा हूं जो आर संदर्भित करता है।

नल पॉइंटर के बारे में आपकी टिप्पणी भी गलत है। नल पॉइंटर को अपरिवर्तित करने से अपरिभाषित व्यवहार होता है - यह क्रैश में हो सकता है या नहीं।

+0

क्या आप कहने का मतलब है कि सी ++ में "पास बाय पॉइंटर" जैसी कोई चीज़ मौजूद नहीं है? यद्यपि आप सही हैं, मुझे लगता है कि सवाल यह है कि एक सूचक या संदर्भ को _use_ कब करना है। – xtofl

+0

नहीं, मुझे यह कहना नहीं है। अस्थायी चर के लिए –

0

आप एक समारोह है कि सूचक द्वारा एक चर हो जाता है लिखते हैं, तो आप सबसे अधिक शायद जाँच करने के लिए करता है, तो सूचक मान्य है होगा (जैसे नहीं NULL) , अन्यथा आप प्रोग्राम दुर्घटना का जोखिम उठाते हैं।

+0

"आप प्रोग्राम क्रैश को जोखिम देते हैं"। स्ट्रैपी जैसे मानक फ़ंक्शन नल की जांच नहीं करते हैं। वे इस विचार को देखते हैं कि यह उनका कॉलर है जो एक दुर्घटना का जोखिम उठा रहा है (एक अवैध मूल्य पारित करके), न कि उन्हें। –

2

इसके लिए नियम संख्या एक: यदि NULL फ़ंक्शन के संदर्भ में फ़ंक्शन पैरामीटर के लिए मान्य मान है, तो इसे पॉइंटर के रूप में पास करें, अन्यथा इसे संदर्भ के रूप में पास करें।

तर्क, यदि यह कभी नहीं हो सकता है (नहीं!), तो न्यूल की जांच करने की परेशानी के माध्यम से खुद को न रखें।

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