2011-01-10 16 views
80

क्या निम्नलिखित कोड (func1()) सही है यदि मुझे वापस करना है? मुझे कहीं और पढ़ना याद है कि एक स्थानीय चर के संदर्भ में एक समस्या है। Func2() से यह अलग कैसे है?सी ++ स्थानीय चर के संदर्भ में संदर्भ

int& func1() 
{ 
    int i; 
    i = 1; 
    return i; 
} 

int* func2() 
{ 
    int* p; 
    p = new int; 
    *p = 1; 
    return p; 
} 
+1

यह सही नहीं है। –

+1

यदि आप गतिशील आवंटित स्मृति का उपयोग करने के लिए func1() को बदलते हैं तो वे समान हैं :-) 'int & i = * new int;' –

+1

@ मार्टिन इसे कौन हटा देगा? – balki

उत्तर

142

यह कोड स्निपेट:

int& func1() 
{ 
    int i; 
    i = 1; 
    return i; 
} 

क्योंकि आप एक जीवन भर के समारोह कॉल के दायरे तक ही सीमित के साथ एक वस्तु के लिए एक उपनाम (एक संदर्भ) लौट रहे हैं काम नहीं करेगा। इसका मतलब है कि एक बार func1() रिटर्न, int i मर जाता है, संदर्भ को निष्पादन से वापस लौटाया गया क्योंकि यह अब किसी ऑब्जेक्ट को संदर्भित करता है जो मौजूद नहीं है।

int main() 
{ 
    int& p = func1(); 
    /* p is garbage */ 
} 

दूसरे संस्करण क्योंकि चर मुक्त दुकान है, जो समारोह कॉल के जीवनकाल के लिए बाध्य नहीं है पर आवंटित किया जाता है काम करता है। हालांकि, आप आवंटित int में delete के लिए ज़िम्मेदार हैं।

int* func2() 
{ 
    int* p; 
    p = new int; 
    *p = 1; 
    return p; 
} 

int main() 
{ 
    int* p = func2(); 
    /* pointee still exists */ 
    delete p; // get rid of it 
} 

आमतौर पर आप कुछ RAII वर्ग और/या एक कारखाने समारोह में सूचक लपेट ताकि आप इसे अपने आप को delete की जरूरत नहीं है जाएगा।

या तो मामले में, तुम बस मूल्य में ही लौट सकते हैं (हालांकि मैं उदाहरण आपके द्वारा दी गई एहसास शायद काल्पनिक था):

int func3() 
{ 
    return 1; 
} 

int main() 
{ 
    int v = func3(); 
    // do whatever you want with the returned value 
} 

ध्यान दें कि यह बड़ा वस्तुओं उसी तरह func3() रिटर्न आदिम वापस जाने के लिए पूरी तरह से ठीक है मूल्यों क्योंकि लगभग हर संकलक आजकल return value optimization के कुछ फार्म लागू करता है:

class big_object 
{ 
public: 
    big_object(/* constructor arguments */); 
    ~big_object(); 
    big_object(const big_object& rhs); 
    big_object& operator=(const big_object& rhs); 
    /* public methods */ 
private: 
    /* data members */ 
}; 

big_object func4() 
{ 
    return big_object(/* constructor arguments */); 
} 

int main() 
{ 
    // no copy is actually made, if your compiler supports RVO 
    big_object o = func4();  
} 

दिलचस्प है, एक स्थिरांक रेफरी के लिए एक अस्थायी बंधन ईरेंस perfectly legal C++ है।

int main() 
{ 
    // This works! The returned temporary will last as long as the reference exists 
    const big_object& o = func4();  
    // This does *not* work! It's not legal C++ because reference is not const. 
    // big_object& o = func4(); 
} 
+1

सुंदर स्पष्टीकरण। : हैटिप: तीसरे कोड स्निपेट में, आप 'int * p = func2() को हटा रहे हैं; पी हटाएं; 'अब, जब आपने' पी 'हटा दिया है, तो क्या इसका मतलब यह है कि फ़ंक्शन' func2() ' की परिभाषा को" अंदर "आवंटित स्मृति भी हटा दी गई है? –

+1

@ अनीशा कौल: हां। स्मृति को 'func2()' के अंदर आवंटित किया गया था और अगली पंक्ति में बाहर जारी किया गया था। हालांकि स्मृति को संभालने के लिए यह एक त्रुटि-प्रवण तरीका है, जैसा कि मैंने कहा था कि आप इसके बजाय RAII के कुछ प्रकार का उपयोग करेंगे। वैसे, आप ऐसा लगता है जैसे आप सी ++ सीख रहे हैं। मैं सीखने के लिए [एक अच्छी प्रारंभिक सी ++ पुस्तक] (http://stackoverflow.com/questions/388242) चुनने की सलाह देता हूं। इसके अलावा, यदि आपके पास कोई प्रश्न है तो भविष्य के संदर्भ के लिए, आप हमेशा स्टैक ओवरफ़्लो पर प्रश्न पोस्ट कर सकते हैं। टिप्पणियां पूरी तरह से नए प्रश्न पूछने के लिए नहीं हैं। –

+0

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

13

एक स्थानीय चर ढेर पर स्मृति है, जब आप स्कोप से बाहर निकलते हैं तो स्मृति स्वचालित रूप से अमान्य नहीं होती है। एक समारोह से गहरा घोंसला (स्मृति में ढेर पर उच्च), यह इस स्मृति तक पहुंचने के लिए पूरी तरह से सुरक्षित है।

एक बार जब फ़ंक्शन वापस आता है और समाप्त होता है, तो चीजें खतरनाक हो जाती हैं। आमतौर पर जब आप वापस आते हैं तो स्मृति को हटाया नहीं जाता है या ओवरराइट नहीं किया जाता है, जिसका अर्थ है कि उस एड्रेस में स्मृति अभी भी आपका डेटा है - सूचक मान्य लगता है।

जब तक कोई अन्य कार्य ढेर का निर्माण नहीं करता है और इसे ओवरराइट करता है। यही कारण है कि यह थोड़ी देर के लिए काम कर सकता है - और फिर अचानक एक विशेष रूप से गहराई से घोंसले हुए कार्यों के सेट के बाद काम करना बंद कर देता है, या वास्तव में बड़े पैमाने पर या कई स्थानीय वस्तुओं के साथ एक समारोह, फिर उस स्टैक-मेमोरी तक पहुंच जाता है।

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

1

याद रखने के लिए एक अच्छी बात ये सरल नियम हैं, और वे दोनों पैरामीटर और रिटर्न प्रकारों पर लागू होते हैं ...

  • मूल्य - प्रश्न में आइटम की एक प्रति बनाता है।
  • पॉइंटर - प्रश्न में आइटम के पते को संदर्भित करता है।
  • संदर्भ - सचमुच प्रश्न में आइटम है।

प्रत्येक के लिए एक समय और स्थान है, इसलिए सुनिश्चित करें कि आप उन्हें जान लें। जैसा कि आपने यहां दिखाया है, स्थानीय वैरिएबल, केवल उस समय तक सीमित हैं, जब तक वे फ़ंक्शन स्कोप में स्थानीय रूप से जीवित नहीं हैं। आपके उदाहरण में int* का रिटर्न प्रकार और &i लौटने पर समान रूप से गलत होता। आप उस मामले में बेहतर होगा ...

void func1(int& oValue) 
{ 
    oValue = 1; 
} 

ऐसा करने से आपके पास पारित पैरामीटर का मूल्य सीधे बदल जाएगा। जबकि यह कोड ...

void func1(int oValue) 
{ 
    oValue = 1; 
} 

नहीं होगा। यह फ़ंक्शन कॉल में oValue स्थानीय का मूल्य बदल देगा। इसका कारण यह है कि आप वास्तव में oValue की "स्थानीय" प्रतिलिपि बदल रहे होंगे, और oValue स्वयं नहीं।

-3
> int &f(); int x; 
> 
> int main() { 
>  f() = 100; 
>  cout<<x; 
>  return 0; } 
> 
> int &f() { 
>  int x; 
>  return x; } 

वापसी एक समारोह च का प्रकार int का एक सूचक है लेकिन यू पूर्णांक चर है कि इतने सूचक नहीं है कि क्यों यह त्रुटि उत्पन्न है लौटने। एफ फ़ंक्शन में पूर्णांक प्रकार के सूचक का उपयोग करें।

+1

यह एक संकेत देता है, सूचक नहीं। स्वीकृत उत्तर सही है। – SirGuy

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