सूचक बनाम संदर्भ


256

कार्य को मूल चर के साथ काम करने के लिए बेहतर अभ्यास क्या होगा:

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

या:

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

IOW: क्या किसी एक को दूसरे पर लेने का कोई कारण है?


1
संदर्भ बेशक मूल्यवान हैं, लेकिन मैं सी से आता हूं, जहां संकेत हर जगह हैं। संदर्भों के मूल्य को समझने के लिए सबसे पहले बिंदुओं से पारंगत होना पड़ता है।
Jay D

कार्यात्मक प्रोग्रामिंग से संदर्भात्मक पारदर्शिता जैसे लक्ष्य के साथ यह कैसे फिट होता है? क्या होगा यदि आप हमेशा नई वस्तुओं को वापस करने के लिए फ़ंक्शन चाहते हैं और कभी भी आंतरिक रूप से राज्य को म्यूट नहीं करते हैं, खासकर फ़ंक्शन में पारित होने वाले चर के नहीं। क्या इस तरह से C ++ जैसी भाषा में संकेत और संदर्भ के साथ इस अवधारणा का उपयोग किया जाता है। (ध्यान दें, मैं मान रहा हूं कि किसी के पास पहले से ही संदर्भित पारदर्शिता का लक्ष्य है। मुझे इस बारे में बात करने में कोई दिलचस्पी नहीं है कि यह एक अच्छा लक्ष्य है या नहीं।)
ely

संदर्भों को प्राथमिकता दें। जब आपके पास कोई विकल्प न हो तो उपयोगकर्ता संकेत दें।
फेरुचियो

जवाबों:


285

अंगूठे का मेरा नियम है:

यदि आप उनके साथ सूचक अंकगणित करना चाहते हैं तो पॉइंटर्स का उपयोग करें (उदाहरण के लिए किसी सरणी से होकर जाने के लिए सूचक पते को बढ़ाना) या यदि आपको कभी NULL-pointer पास करना है।

संदर्भों का उपयोग करें अन्यथा।


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

26
अंकगणित से आपका क्या अभिप्राय है। एक नया उपयोगकर्ता यह नहीं समझ सकता है कि आप इंगित करना चाहते हैं कि सूचक किस ओर इशारा कर रहा है।
मार्टिन यॉर्क

7
मार्टिन, अंकगणित से मेरा मतलब है कि आप एक संरचना के लिए एक सूचक पास करते हैं लेकिन जानते हैं कि यह एक साधारण संरचना नहीं है, बल्कि इसका एक सरणी है। इस स्थिति में या तो आप इसे [] का उपयोग करके अनुक्रमित कर सकते हैं या सूचक पर ++ / - का उपयोग करके अंकगणित कर सकते हैं। यह संक्षेप में अंतर है।
Nils Pipenbrinck

2
मार्टिन, आप केवल सीधे संकेत के साथ ऐसा कर सकते हैं। संदर्भों के साथ नहीं। यकीन है कि आप एक संदर्भ के लिए एक संकेतक ले सकते हैं और अभ्यास में एक ही काम कर सकते हैं, लेकिन यदि आप ऐसा करते हैं तो आप बहुत गंदे कोड के साथ समाप्त होते हैं ..
निल्स पिपेनब्रिंक

1
बहुरूपता (जैसे Base* b = new Derived()) के बारे में क्या ? यह एक ऐसे मामले की तरह प्रतीत होता है जिसे बिना संकेत के संभाला नहीं जा सकता।
क्रिस रेडफोर्ड

72

मुझे वास्तव में लगता है कि आप निम्नलिखित दिशा-निर्देशों को कॉलिंग दिशानिर्देशों की स्थापना से लाभान्वित करेंगे:

  1. अन्य सभी स्थानों की तरह, हमेशा-सही होना constचाहिए।

    • नोट: इसका मतलब है, अन्य चीजों के अलावा, केवल आउट-वैल्यू (आइटम 3 देखें) और वैल्यू द्वारा पास किए गए मान (आइटम 4 देखें) में स्पेसियर की कमी हो सकती है const
  2. केवल पॉइंटर से कोई मान पास करें यदि मान 0 / NULL वर्तमान संदर्भ में एक मान्य इनपुट है।

    • तर्क 1: एक फोन करने वाले के रूप में , आप देखते हैं कि जो कुछ भी आप पास करते हैं वह एक उपयोगी स्थिति में होना चाहिए

    • तर्क 2: जैसा कि कहा जाता है , आप जानते हैं कि जो भी आता है वह प्रयोग करने योग्य अवस्था में है। इसलिए, उस मूल्य के लिए किसी भी NULL-check या त्रुटि से निपटने की आवश्यकता नहीं है।

    • तर्क 3: तर्क 1 और 2 संकलित होंगे । यदि आप कर सकते हैं हमेशा संकलन समय पर त्रुटियों को पकड़ें।

  3. यदि कोई फ़ंक्शन तर्क आउट-मान है, तो इसे संदर्भ द्वारा पास करें।

    • तर्क: हम आइटम 2 को तोड़ना नहीं चाहते ...
  4. "वैल्यू बाय पास" को "कॉन्स्ट रिफरेंस द्वारा पास" तभी चुनें जब वैल्यू एक POD ( प्लेन पुराना डेटास्ट्रक्चर ) या छोटा पर्याप्त (मेमोरी-वार) या अन्य तरीकों से कॉपी करने के लिए पर्याप्त (समय-वार) सस्ता हो।

    • तर्क: अनावश्यक प्रतियों से बचें।
    • नोट: छोटे पर्याप्त और सस्ते पर्याप्त पूर्ण औसत दर्जे के नहीं हैं।

इसमें दिशानिर्देशों का अभाव होता है: ... "जब कांस्टेबल का उपयोग करना है" और ... दिशानिर्देश 2 में "[इन] मानों के लिए लिखा जाना चाहिए, केवल सूचक द्वारा पास करें यदि NULL मान्य है। अन्यथा, कॉन्स्ट संदर्भ (या" के लिए) का उपयोग करें। छोटे "ऑब्जेक्ट्स, कॉपी), या संदर्भ अगर यह एक [आउट] मान है। मैं संभावित रूप से +1 जोड़ने के लिए इस पोस्ट की निगरानी कर रहा हूं।
11 अक्टूबर को paercebal

आइटम 1 आपके द्वारा वर्णित केस को कवर करता है।
जोहान गेरेल

यदि यह डिफ़ॉल्ट-निर्माण योग्य नहीं है, तो संदर्भ द्वारा आउट-पैरामीटर पास करना थोड़ा कठिन है। यह मेरे कोड में काफी सामान्य है - एक समारोह होने का पूरा कारण यह है कि आउट-ऑब्जेक्ट है क्योंकि यह गैर-तुच्छ है।
MSalters

@MSalters: यदि आप फ़ंक्शन के अंदर मेमोरी को आवंटित करने जा रहे हैं (जो मुझे लगता है कि आपका क्या मतलब है), तो क्यों न केवल आवंटित मेमोरी में एक पॉइंटर लौटाएं?
क्लीस्ट

@Kleist: @MSalters की ओर से, कई संभावित कारण हैं। एक यह है कि आपको पहले से ही भरने के लिए मेमोरी आवंटित की जा सकती है, पूर्व आकार की तरह std::vector<>
जोहान गेरेल

24

यह अंत में व्यक्तिपरक हो रहा है। इस प्रकार चर्चा बहुत उपयोगी है, लेकिन मुझे नहीं लगता कि इसका कोई सही या निर्णायक उत्तर है। बहुत कुछ शैली के दिशानिर्देशों और उस समय आपकी आवश्यकताओं पर निर्भर करेगा।

जबकि पॉइंटर के साथ कुछ अलग क्षमताएं (कुछ हो या न हो) पूरी तरह से होती हैं, आउटपुट पैरामीटर के लिए सबसे बड़ा व्यावहारिक अंतर पूरी तरह से सिंटैक्स होता है। Google का C ++ स्टाइल गाइड ( https://google.github.io/styleguide/cppguide.html#Reference_Arguments ), उदाहरण के लिए, आउटपुट पैरामीटर के लिए केवल संकेत देता है, और केवल ऐसे संदर्भों की अनुमति देता है जो कॉन्स्टेंट हैं। तर्क एक पठनीयता है: मूल्य सिंटैक्स के साथ कुछ सूचक अर्थ अर्थ नहीं होना चाहिए। मैं सुझाव नहीं दे रहा हूं कि यह जरूरी सही है या गलत है, लेकिन मुझे लगता है कि यहां मुद्दा यह है कि यह स्टाइल की बात है, शुद्धता की नहीं।


इसका क्या मतलब है कि संदर्भों में मूल्य सिंटैक्स है लेकिन सूचक अर्थ अर्थ है?
एरिक एंड्रयू लुईस

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

किसी को यह नहीं भूलना चाहिए कि Google C ++ शैली मार्गदर्शिका बहुत हिरासत में है।
डिडुप्लिकेटर

7

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


2
यदि आप जोहान गेरेल दिशानिर्देशों का पालन करते हैं, तो एक गैर-कॉन्स्टेंस संदर्भ भी परिवर्तनशील चर का विज्ञापन करता है, इसलिए सूचक के पास यहां लाभ नहीं है।
अलेक्जेंडर कोंड्रेट्सकी जुएल 19'11

4
@AlexanderKondratskiy: आप इस बिंदु को याद कर रहे हैं ... आप कॉल साइट पर तुरंत नहीं देख सकते हैं कि क्या कहा गया फ़ंक्शन एक constया गैर- constसंदर्भ के रूप में एक पैरामीटर को स्वीकार करता है , लेकिन आप देख सकते हैं कि पैरामीटर का पारित &xबनाम बनाम x, और उपयोग करें यह समझाने के लिए कि क्या पैरामीटर्स के उत्तरदायी को संशोधित किया जाना है। (उस ने कहा, ऐसे समय होते हैं जब आप एक constपॉइंटर पास करना चाहते हैं , इसलिए कनविंसेंस सिर्फ एक संकेत है। कुछ संदिग्ध होने पर संदेह संशोधित किया जा सकता है, जब यह कम खतरनाक नहीं होगा, यह सोचने की तुलना में यह तब नहीं होगा जब यह होगा। ....)
टोनी डेलारॉय

5

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

ज्यादातर मामलों में एक बेहतर समाधान (सुरक्षा दृष्टिकोण से) को बढ़ावा देने के लिए उपयोग किया जाता है :: वैकल्पिक । यह आपको संदर्भ द्वारा वैकल्पिक मानों में और रिटर्न मान के रूप में पास करने की अनुमति देता है।

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}

5

जब आप कर सकते हैं तो एक संदर्भ का उपयोग करें, जब आपको करना हो तो एक सूचक का उपयोग करें। से सी ++ पूछे जाने वाले प्रश्न: "जब मैं संदर्भ का उपयोग करना चाहिए, और जब मैं संकेत का उपयोग करना चाहिए?"


4

संकेत

  • एक पॉइंटर एक वैरिएबल है जो मेमोरी एड्रेस रखता है।
  • एक सूचक घोषणा में एक आधार प्रकार, * और चर नाम होता है।
  • एक पॉइंटर जीवनकाल में किसी भी संख्या में चर को इंगित कर सकता है
  • एक सूचक जो वर्तमान में एक वैध मेमोरी लोकेशन की ओर इशारा नहीं करता है, उसे वैल्यू नल दिया जाता है (जो शून्य है)

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
  • The & एक unary ऑपरेटर है जो अपने ऑपरेंड के मेमोरी एड्रेस को लौटाता है।

  • डेरेफेरिंग ऑपरेटर (*) का उपयोग चर में संग्रहीत मान तक पहुंचने के लिए किया जाता है जो पॉइंटर पॉइंट करता है।

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;

संदर्भ

  • एक संदर्भ (और) एक मौजूदा चर के लिए एक उपनाम की तरह है।

  • एक संदर्भ (&) एक स्थिर पॉइंटर की तरह है जो स्वचालित रूप से डिरेल हो जाता है।

  • यह आमतौर पर फ़ंक्शन तर्क सूचियों और फ़ंक्शन रिटर्न मानों के लिए उपयोग किया जाता है।

  • जब इसे बनाया जाता है तो एक संदर्भ आरम्भ किया जाना चाहिए।

  • एक बार जब किसी ऑब्जेक्ट को किसी संदर्भ में आरम्भ किया जाता है, तो उसे किसी अन्य ऑब्जेक्ट के संदर्भ में नहीं बदला जा सकता है।

  • आपके पास NULL संदर्भ नहीं हो सकते।

  • एक कॉन्स्ट रेफरेंस एक कॉन्स्टेक्ट इंट को संदर्भित कर सकता है। यह कॉस्ट के मान के साथ एक अस्थायी चर के साथ किया जाता है

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें


3

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


3

C # के आउट कीवर्ड पर विचार करें। कंपाइलर को आउट कीवर्ड को किसी भी आउट आर्ग पर लागू करने के लिए एक विधि के कॉलर की आवश्यकता होती है, भले ही यह पहले से ही पता हो कि वे क्या हैं। यह पठनीयता बढ़ाने का इरादा है। हालांकि आधुनिक आईडीई के साथ मुझे लगता है कि यह वाक्य रचना (या अर्थ) हाइलाइटिंग के लिए एक काम है।


टाइपो: सिमेंटिक, सिम्मेटिक नहीं; +1 मैं लिखने के बजाय हाइलाइट करने की संभावना के बारे में सहमत हूं (C #), या & (C के मामले में, कोई संदर्भ नहीं)
पीनट

2

जब तक कोई कारण न हो कि आप जिस सामग्री को बदलना चाहते हैं, उसे पास करें।

यह ज्यादातर मामलों में सबसे कुशल विधि होगी।

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


2

संकेत:

  • सौंपा जा सकता है nullptr(या NULL)।
  • कॉल साइट पर, आपको उपयोग करना होगा &यदि आपका प्रकार स्वयं एक संकेतक नहीं है, तो स्पष्ट रूप से आप अपनी वस्तु को संशोधित कर रहे हैं।
  • पॉइंटर्स रिबाउंड हो सकते हैं।

संदर्भ:

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

जो लोग नहीं जानते के लिए एक छोटा सा बिंदु: nullptr या शून्य बस एक 0. है stackoverflow.com/questions/462165/...
Serguei फेदोरोव

2
nullptr 0. के समान नहीं है। int int a = nullptr; stackoverflow.com/questions/1282295/what-exactly-is-nullptr
जोहान लुंडबर्ग

0

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

फ़ंक्शन तर्कों को निर्दिष्ट करने के लिए संदर्भ विशेष रूप से उपयोगी हैं।

अधिक जानकारी के लिए "बजरन स्ट्रॉस्ट्रुप" (2014) पृष्ठ 11-12 द्वारा "ए टूर ऑफ सी ++" देखें

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