C ++ स्थानीय चर का संदर्भ लौटाता है


117

यदि मुझे वापस जाना है तो क्या निम्न कोड (func1 ()) सही है? मुझे याद है कि एक स्थानीय चर के संदर्भ में लौटते समय एक समस्या है। यह func2 () से कैसे भिन्न है?

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

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

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

जवाबों:


193

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

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

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

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

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

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()आदिम मूल्यों को लौटाता है क्योंकि आजकल हर कंपाइलर के बारे में रिटर्न वैल्यू ऑप्टिमाइजेशन के कुछ फॉर्म लागू होते हैं :

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();    
}

दिलचस्प है, एक अस्थायी को एक कास्ट संदर्भ में बांधना पूरी तरह से कानूनी सी ++ है

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();  
}

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

2
@ अनिशा कौल: हां। मेमोरी को अंदर आवंटित किया गया था func2()और अगली पंक्ति में बाहर जारी किया गया था। यह स्मृति को संभालने के लिए एक त्रुटि-रहित तरीका है, हालांकि, जैसे मैंने कहा कि आप इसके बजाय RAII के कुछ संस्करण का उपयोग करेंगे। वैसे, आपको लगता है जैसे आप C ++ सीख रहे हैं। मैं सीखने के लिए एक अच्छी परिचयात्मक C ++ पुस्तक लेने की सलाह देता हूं । इसके अलावा, भविष्य के संदर्भ के लिए यदि आपके पास कोई प्रश्न है, तो आप हमेशा स्टैक ओवरफ्लो पर प्रश्न पोस्ट कर सकते हैं। टिप्पणियाँ पूरी तरह से नए प्रश्न पूछने के लिए नहीं हैं।
सिलिको

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

और आपने उत्तर संपादित किया है ?? : पागल: मैं इसे आसानी से याद कर सकता था। ;);)
Aquarius_Girl

@ अनिशा कौल: नहीं, मैंने नहीं किया। पिछली बार जब मैंने अपना उत्तर संपादित किया था, तो वह १० जनवरी को मेरे पद के तहत समय की मुहर के अनुसार था।
सिलिको

18

स्टैक पर एक स्थानीय चर मेमोरी है, जब आप दायरे से बाहर जाते हैं तो मेमोरी स्वचालित रूप से अमान्य नहीं होती है। एक फ़ंक्शन से नेस्टेड नेस्ट (मेमोरी में स्टैक पर अधिक), इस मेमोरी तक पहुंचने के लिए पूरी तरह से सुरक्षित है।

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

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

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


2

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

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

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

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

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

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.