C ++ में संदर्भ द्वारा एक सूचक पास करने का कारण?


97

आप किन परिस्थितियों में इस प्रकृति के कोड का उपयोग करना चाहते हैं c ++?

void foo(type *&in) {...}

void fii() {
  type *choochoo;
  ...
  foo(choochoo);
}

अगर आपको एक पॉइंटर वापस करने की आवश्यकता है - बेहतर वापसी मूल्य का उपयोग करें
littleadv

6
क्या आप इस बारे में खुलासा कर सकते हैं? यह सराहनीय मददगार नहीं है। मेरा सवाल काफी वैध है। वर्तमान में इसका उपयोग उत्पादन कोड में किया जा रहा है। मुझे अभी पूरी तरह से समझ में नहीं आया है कि क्यों।
मैथ्यू होगन

1
डेविड ने इसे अच्छी तरह से गाया है। सूचक को ही संशोधित किया जा रहा है।
क्रिस

2
मैं इस तरह से गुजरता हूं अगर मुझे फ़ंक्शन में "नया" ऑपरेटर कहा जाएगा।
NDEthos

जवाबों:


142

यदि आप पॉइंटर को इंगित करने वाले ऑब्जेक्ट के बजाय पॉइंटर को संशोधित करने की आवश्यकता है, तो आप संदर्भ द्वारा एक पॉइंटर पास करना चाहेंगे।

यह इसी वजह से है कि डबल पॉइंटर्स का उपयोग क्यों किया जाता है; पॉइंटर के संदर्भ में पॉइंटर्स का उपयोग करने की तुलना में थोड़ा सुरक्षित है।


14
एक पॉइंटर के पॉइंटर के समान, पास-दर-संदर्भ अर्थ-विज्ञान के लिए अप्रत्यक्ष के एक कम स्तर को छोड़कर?

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

2
@William आप उस पर विस्तृत कर सकते हैं? यह दिलचस्प लगता है। क्या इसका मतलब यह है कि उस वर्ग का उपयोगकर्ता आंतरिक डेटा बफर को एक पॉइंटर प्राप्त कर सकता है और उसे पढ़ / लिख सकता है, लेकिन यह नहीं हो सकता है - उदाहरण के लिए - उस बफर का उपयोग deleteया उस मेमोरी में किसी अन्य स्थान पर पॉइंटर को रिबंड करने के लिए? या मुझे गलत लगा?
बारबराकर्क

2
@BarbaraKwarc ज़रूर। कहें कि आपके पास एक गतिशील सरणी का एक सरल कार्यान्वयन है। स्वाभाविक रूप से आप []ऑपरेटर को अधिभारित करेंगे । इसके लिए एक संभव (और वास्तव में स्वाभाविक) हस्ताक्षर होगा const T &operator[](size_t index) const। लेकिन आप भी कर सकते थे T &operator[](size_t index)। आप एक ही समय में दोनों हो सकते हैं। बाद वाला आपको चीजों को करने देगा myArray[jj] = 42। उसी समय, आप अपने डेटा को पॉइंटर प्रदान नहीं कर रहे हैं, इसलिए कॉल करने वाला मेमोरी में गड़बड़ी नहीं कर सकता (उदाहरण के लिए इसे गलती से हटा दें)।

ओह। मुझे लगा कि आप एक पॉइंटर के संदर्भ में लौटने की बात कर रहे हैं (आपका सटीक शब्द: "एक पॉइंटर को संदर्भ द्वारा लौटाएं", क्योंकि यही ओपी के बारे में पूछ रहा था: संदर्भों से गुजरते हुए, न केवल सादे पुराने संदर्भों में) कुछ फैंसी तरीके से बफर को हेरफेर करने की अनुमति के बिना बफर को रीड / राइट एक्सेस देना (जैसे इसे फ्री करना)। एक सादे पुराने संदर्भ को वापस करना एक अलग बात है और मुझे यह पता है।
बारबराकर्क

65

सी ++ प्रोग्रामर का 50% अपने डिलीटर्स को डिलीट करने के बाद सेट करना पसंद करते हैं:

template<typename T>    
void moronic_delete(T*& p)
{
    delete p;
    p = nullptr;
}

संदर्भ के बिना, आप केवल सूचक की एक स्थानीय प्रति बदल रहे होंगे, कॉल करने वाले को प्रभावित नहीं करेंगे।


19
मुझे नाम पसंद है; )
डब्लू

2
मैं उत्तर का पता लगाने के लिए बेवकूफ लगने का व्यापार करूंगा: इसे मोरोनिक डिलीट क्यों कहा जाता है? मैं क्या खो रहा हूँ?
सम्मरन

1
@Sammaron यदि आप इतिहास को देखते हैं, तो फ़ंक्शन टेम्पलेट का उपयोग किया जाता था paranoid_delete, लेकिन पिल्ला ने इसका नाम बदल दिया moronic_delete। वह संभवत: अन्य 50% से संबंधित है;) वैसे भी, संक्षिप्त उत्तर यह है कि सेटिंग पॉइंटर्स को शून्य करने के deleteलिए लगभग कभी भी उपयोगी नहीं है (क्योंकि उन्हें गुंजाइश से बाहर जाना चाहिए, वैसे भी) और अक्सर "मुफ्त के बाद उपयोग" की पहचान में बाधा उत्पन्न होती है।
fredoverflow

@fredoverflow मुझे आपकी प्रतिक्रिया समझ में आती है, लेकिन @ पप्पी को लगता deleteहै कि सामान्य रूप से बेवकूफ है। पिल्ला, मैंने कुछ गुगली किया, मैं नहीं देख सकता कि क्यों पूरी तरह से बेकार है (शायद मैं एक भयानक गुग्लर भी हूं;))। क्या आप थोड़ा और समझा सकते हैं या एक लिंक प्रदान कर सकते हैं?
सैममोरन

@Sammaron, C ++ में अब "स्मार्ट पॉइंटर्स" हैं जो नियमित पॉइंटर्स को एन्कैप करते हैं और स्कोप से बाहर जाने पर स्वचालित रूप से आपके लिए "डिलीट" कहते हैं। स्मार्ट-पॉइंटर्स को सी-स्टाइल डंब पॉइंटर्स पर अधिक पसंद किया जाता है क्योंकि वे इस समस्या को पूरी तरह से खत्म कर देते हैं।
tjwrona1992

14

डेविड का जवाब सही है, लेकिन अगर यह अभी भी थोड़ा सार है, तो यहां दो उदाहरण हैं:

  1. आप स्मृति समस्याओं को पकड़ने के लिए सभी मुक्त संकेत शून्य करना चाहते हैं। सी-स्टाइल आप करेंगे:

    void freeAndZero(void** ptr)
    {
        free(*ptr);
        *ptr = 0;
    }
    
    void* ptr = malloc(...);
    
    ...
    
    freeAndZero(&ptr);
    

    C ++ में भी ऐसा ही करने के लिए, आप यह कर सकते हैं:

    template<class T> void freeAndZero(T* &ptr)
    {
        delete ptr;
        ptr = 0;
    }
    
    int* ptr = new int;
    
    ...
    
    freeAndZero(ptr);
    
  2. लिंक-सूचियों से निपटने के दौरान - अक्सर केवल अगले नोड के संकेत के रूप में प्रतिनिधित्व किया जाता है:

    struct Node
    {
        value_t value;
        Node* next;
    };
    

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

    void insert(Node* &list)
    {
        ...
        if(!list) list = new Node(...);
        ...
    }
    

इस प्रश्न में एक उदाहरण है ।


8

मुझे इस तरह से कोड का उपयोग करना पड़ा है ताकि पास में रखे पॉइंटर को मेमोरी आवंटित करने के लिए फ़ंक्शंस प्रदान की जा सके और एसटीएल का उपयोग करके मेरी कंपनी "ऑब्जेक्ट" मुझे वापस कर सके।

 int iSizeOfArray(int* &piArray) {
    piArray = new int[iNumberOfElements];
    ...
    return iNumberOfElements;
 }

यह अच्छा नहीं है, लेकिन पॉइंटर को संदर्भ द्वारा पारित किया जाना चाहिए (या डबल पॉइंटर का उपयोग करें)। यदि नहीं, तो मेमोरी को पॉइंटर की एक स्थानीय कॉपी के लिए आवंटित किया जाता है यदि यह मूल्य द्वारा पारित किया जाता है जिसके परिणामस्वरूप मेमोरी रिसाव होता है।


2

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

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


क्यों होता है पतन? क्या मैंने जो लिखा है, उसमें कुछ गड़बड़ है? समझाने की परवाह? : पी
बारबराक्वार्क

0

एक और स्थिति जब आपको इसकी आवश्यकता हो सकती है यदि आपके पास पॉइंटर्स का संग्रह है और स्टाल एल्गोरिदम का उपयोग करके उन्हें बदलना चाहते हैं। C ++ 98 में for_each का उदाहरण।

struct Storage {
  typedef std::list<Object*> ObjectList;
  ObjectList objects;

  void change() {
    typedef void (*ChangeFunctionType)(Object*&);
    std::for_each<ObjectList::iterator, ChangeFunctionType>
                 (objects.begin(), objects.end(), &Storage::changeObject);
  }

  static void changeObject(Object*& item) {
    delete item;
    item = 0;
    if (someCondition) item = new Object();
  } 

};

अन्यथा, यदि आप changeObject (ऑब्जेक्ट * आइटम) हस्ताक्षर का उपयोग करते हैं तो आपके पास सूचक की प्रतिलिपि है, मूल नहीं।


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