c++ सी++ में पॉइंटर चर और संदर्भ चर के बीच अंतर क्या हैं?




pointers reference (24)

मुझे पता है कि संदर्भ वाक्य रचनात्मक चीनी हैं, इसलिए कोड पढ़ने और लिखना आसान है।

लेकिन मतभेद क्या हैं?

उत्तर और नीचे दिए गए लिंक से सारांश:

  1. किसी सूचक को बाध्यकारी के बाद फिर से असाइन नहीं किया जा सकता है, जबकि एक सूचक को कई बार फिर से असाइन किया जा सकता है।
  2. पॉइंटर्स कहीं भी ( NULL ) इंगित कर सकते हैं, जबकि एक संदर्भ हमेशा एक वस्तु को संदर्भित करता है।
  3. आप पॉइंटर्स के साथ संदर्भ के पते को नहीं ले सकते हैं।
  4. कोई "संदर्भ अंकगणितीय" नहीं है (लेकिन आप संदर्भ द्वारा इंगित ऑब्जेक्ट का पता ले सकते हैं और पॉइंटर अंकगणित पर उस पर &obj + 5 ) कर सकते हैं।

एक गलतफहमी को स्पष्ट करने के लिए:

सी ++ मानक यह समझने से बचने के लिए बहुत सावधान है कि एक कंपाइलर संदर्भ कैसे कार्यान्वित कर सकता है, लेकिन प्रत्येक सी ++ कंपाइलर संदर्भ पॉइंटर्स के रूप में लागू करता है। यही है, एक घोषणा जैसे कि:

int &ri = i;

यदि यह पूरी तरह से अनुकूलित नहीं किया गया है , तो एक ही मात्रा में भंडारण को एक सूचक के रूप में आवंटित करता है, और उस भंडारण में i पता लगाता है।

तो, एक सूचक और संदर्भ दोनों एक ही मात्रा में स्मृति का उपयोग करते हैं।

एक सामान्य नियम के रूप में,

  • उपयोगी और स्वयं-दस्तावेज इंटरफेस प्रदान करने के लिए फ़ंक्शन पैरामीटर और रिटर्न प्रकारों में संदर्भों का उपयोग करें।
  • एल्गोरिदम और डेटा संरचनाओं को लागू करने के लिए पॉइंटर्स का उपयोग करें।

दिलचस्प पढ़ना:


मुझे लगता है कि अभी तक एक और बिंदु है जिसे यहां शामिल नहीं किया गया है।

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

हालांकि यह सतही दिखाई दे सकता है, मेरा मानना ​​है कि यह संपत्ति कई सी ++ सुविधाओं के लिए महत्वपूर्ण है, उदाहरण के लिए:

  • टेम्पलेट्स । के बाद से टेम्प्लेट पैरामीटर बतख टाइप कर रहे हैं, एक प्रकार का वाक्यात्मक गुण सब मायने रखता है, इसलिए अक्सर एक ही टेम्पलेट दोनों के साथ इस्तेमाल किया जा सकता Tहै और T&
    (या std::reference_wrapper<T>जो अभी भी एक निहित कास्ट पर निर्भर करता है T&)
    टेम्पलेट जो दोनों को कवर करते हैं T&और T&&इससे भी अधिक आम हैं।

  • Lvalues । बयान पर विचार करें str[0] = 'X';संदर्भों के बिना यह केवल सी-स्ट्रिंग्स ( char* str) के लिए काम करेगा । संदर्भ द्वारा चरित्र को लौटने से उपयोगकर्ता परिभाषित कक्षाओं को एक ही संकेत मिल सकता है।

  • रचनाकारों की प्रतिलिपि बनाएँ । संवैधानिक रूप से यह रचनाकारों की प्रतिलिपि बनाने के लिए वस्तुओं को पारित करने के लिए समझ में आता है, और वस्तुओं को पॉइंटर्स नहीं। लेकिन प्रतिलिपि बनाने के लिए प्रतिलिपि बनाने के लिए कोई प्रतिलिपि नहीं है - इसके परिणामस्वरूप एक ही प्रतिलिपि निर्माता को रिकर्सिव कॉल किया जाएगा। यह यहां एकमात्र विकल्प के रूप में संदर्भ छोड़ देता है।

  • ऑपरेटर अधिभार । संदर्भों के साथ, operator+(const T& a, const T& b)एक ही इंफिक्स नोटेशन को बनाए रखते हुए , एक ऑपरेटर कॉल के लिए संकेत देना शुरू करना संभव है। यह नियमित अधिभारित कार्यों के लिए भी काम करता है।

ये बिंदु सी ++ और मानक पुस्तकालय का एक बड़ा हिस्सा सशक्त करते हैं, इसलिए यह संदर्भों की एक बड़ी संपत्ति है।


एक संदर्भ कभी भी पूर्ण नहीं हो सकता NULL


असल में, एक संदर्भ वास्तव में एक सूचक की तरह नहीं है।

एक कंपाइलर चर के लिए "संदर्भ" रखता है, एक नाम को स्मृति पते के साथ जोड़ता है; संकलन करते समय किसी भी परिवर्तनीय नाम को मेमोरी एड्रेस में अनुवाद करने का यह काम है।

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

पॉइंटर्स चर हैं; उनमें कुछ अन्य चर का पता होता है, या शून्य हो सकता है। महत्वपूर्ण बात यह है कि एक सूचक के पास मूल्य होता है, जबकि संदर्भ में केवल एक चर होता है जो संदर्भित करता है।

अब वास्तविक कोड के कुछ स्पष्टीकरण:

int a = 0;
int& b = a;

यहां आप एक और चर नहीं बना रहे हैं जो एक को इंगित करता a ; आप बस एक के मूल्य वाले स्मृति सामग्री में एक और नाम जोड़ रहे हैं। इस मेमोरी में अब दो नाम हैं, a और b , और इसे किसी भी नाम का उपयोग करके संबोधित किया जा सकता है।

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

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

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


वाक्य रचनात्मक चीनी के अलावा, एक संदर्भ एक const सूचक (एक const सूचक नहीं ) है। जब आप संदर्भ चर घोषित करते हैं, तो आपको यह निर्धारित करना होगा कि यह संदर्भित करता है, और आप इसे बाद में नहीं बदल सकते हैं।

अपडेट करें: अब जब मैं इसके बारे में कुछ और सोचता हूं, तो एक महत्वपूर्ण अंतर होता है।

एक कॉन्स पॉइंटर का लक्ष्य अपना पता ले कर और एक कॉन्स कास्ट का उपयोग करके प्रतिस्थापित किया जा सकता है।

किसी संदर्भ के लक्ष्य को यूबी से कम किसी भी तरह से बदला नहीं जा सकता है।

यह संकलक को संदर्भ पर अधिक अनुकूलन करने की अनुमति देनी चाहिए।


  1. एक सूचक को फिर से सौंपा जा सकता है:

    int x = 5;
    int y = 6;
    int *p;
    p =  &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);
    

    एक संदर्भ प्रारंभ नहीं कर सकता है, और इसे आवंटित किया जाना चाहिए:

    int x = 5;
    int y = 6;
    int &r = x;
    
  2. एक पॉइंटर का अपना मेमोरी पता और आकार स्टैक पर होता है (x86 पर 4 बाइट्स), जबकि एक संदर्भ एक ही मेमोरी एड्रेस (मूल चर के साथ) साझा करता है लेकिन स्टैक पर कुछ जगह भी लेता है। चूंकि संदर्भ में मूल चर के समान ही पता है, इसलिए एक संदर्भ के बारे में सोचने के लिए एक ही चर के लिए एक और नाम है। नोट: स्टैक या ढेर पर क्या पॉइंटर पॉइंट हो सकता है। एक संदर्भ के लिए। इस कथन में मेरा दावा यह नहीं है कि एक सूचक को ढेर को इंगित करना चाहिए। एक सूचक केवल एक चर है जिसमें स्मृति पता होता है। यह चर ढेर पर है। चूंकि किसी संदर्भ में स्टैक पर अपनी जगह होती है, और चूंकि पता वैरिएबल के संदर्भ में समान होता है। ढेर बनाम ढेर पर अधिक। इसका तात्पर्य है कि संदर्भ का वास्तविक पता है कि संकलक आपको नहीं बताएगा।

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
    
  3. आप संकेतक के अतिरिक्त स्तर की पेशकश करने वाले पॉइंटर्स को पॉइंटर्स पॉइंटर्स कर सकते हैं। जबकि संदर्भ केवल एक स्तर का संकेत प्रदान करते हैं।

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
    
  4. सूचक सीधे nullptr असाइन किया जा सकता है, जबकि संदर्भ नहीं कर सकते हैं। यदि आप काफी मेहनत करते हैं, और आप जानते हैं कि, आप संदर्भ nullptr पता कैसे बना सकते हैं। इसी तरह, यदि आप काफी मेहनत करते हैं तो आप एक सूचक के संदर्भ में हो सकते हैं, और फिर उस संदर्भ में nullptr हो nullptr

    int *p = nullptr;
    int &r = nullptr; <--- compiling error
    int &r = *p;  <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
    
  5. पॉइंटर्स एक सरणी पर फिर से सक्रिय हो सकते हैं, आप अगले आइटम पर जाने के लिए ++ का उपयोग कर सकते हैं, जो एक पॉइंटर इंगित कर रहा है, और + 4 5 वें तत्व पर जाने के लिए। इससे कोई फर्क नहीं पड़ता कि ऑब्जेक्ट का आकार क्या है कि पॉइंटर इंगित करता है।

  6. एक पॉइंटर को उस स्मृति स्थान तक पहुंचने के लिए * साथ संदर्भित करने की आवश्यकता है, जबकि संदर्भ को सीधे उपयोग किया जा सकता है। कक्षा / संरचना के लिए एक सूचक -> इसके सदस्यों तक पहुंचने के लिए, जबकि एक संदर्भ का उपयोग करता है .

  7. एक सूचक एक चर है जिसमें स्मृति पता होता है। किसी संदर्भ को कैसे कार्यान्वित किया जाता है, भले ही किसी संदर्भ में वही स्मृति पता हो जो आइटम संदर्भित करता हो।

  8. संदर्भों को एक सरणी में भर नहीं किया जा सकता है, जबकि पॉइंटर्स हो सकते हैं (उपयोगकर्ता @litb द्वारा उल्लिखित)

  9. कॉन्स संदर्भ अस्थायी लोगों के लिए बाध्य किया जा सकता है। पॉइंटर्स नहीं कर सकते (कुछ संकेत के बिना नहीं):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.
    

    यह तर्क सूचियों में और आगे के लिए उपयोग के लिए const& सुरक्षित बनाता है।


एक संदर्भ कुछ स्मृति को दिया गया कोई दूसरा नाम नहीं है। यह एक अपरिवर्तनीय सूचक है जिसे स्वचालित रूप से उपयोग पर संदर्भित किया जाता है। असल में यह नीचे उबलता है:

int& j = i;

यह आंतरिक रूप से बन जाता है

int* const j = &i;

सूचक और संदर्भ के बीच अंतर

एक सूचक को 0 और प्रारंभिक संदर्भ में प्रारंभ किया जा सकता है। वास्तव में, एक संदर्भ को किसी ऑब्जेक्ट को भी संदर्भित करना चाहिए, लेकिन एक पॉइंटर शून्य सूचक हो सकता है:

int* p = 0;

लेकिन हमारे पास int& p = 0;भी नहीं हो सकता हैint& p=5 ;

वास्तव में इसे सही तरीके से करने के लिए, हमने पहले किसी ऑब्जेक्ट को घोषित और परिभाषित किया होगा, फिर हम उस ऑब्जेक्ट का संदर्भ दे सकते हैं, इसलिए पिछले कोड का सही कार्यान्वयन होगा:

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

एक अन्य महत्वपूर्ण बात यह है कि हम प्रारंभकर्ता के बिना पॉइंटर की घोषणा कर सकते हैं हालांकि संदर्भ के मामले में ऐसी कोई चीज़ नहीं की जा सकती है जिसे हमेशा परिवर्तनीय या ऑब्जेक्ट के लिए संदर्भ बनाना चाहिए। हालांकि एक सूचक का उपयोग जोखिम भरा होता है इसलिए आम तौर पर हम जांचते हैं कि सूचक वास्तव में कुछ इंगित कर रहा है या नहीं। संदर्भ के मामले में ऐसी कोई जांच आवश्यक नहीं है, क्योंकि हम पहले से ही जानते हैं कि घोषणा के दौरान किसी ऑब्जेक्ट का संदर्भ अनिवार्य है।

एक और अंतर यह है कि पॉइंटर किसी अन्य ऑब्जेक्ट को इंगित कर सकता है हालांकि संदर्भ हमेशा एक ही ऑब्जेक्ट को संदर्भित करता है, चलो इस उदाहरण को लें:

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

एक और बिंदु: जब हमारे पास एक एसटीएल टेम्पलेट जैसे टेम्पलेट होते हैं तो इस प्रकार के क्लास टेम्पलेट ऑपरेटर का उपयोग करके आसानी से पढ़ने या नए मूल्य को असाइन करने के लिए हमेशा एक संकेत नहीं, एक संकेतक वापस करेगा, []:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="

भ्रम में जोड़ने के जोखिम पर, मैं कुछ इनपुट में फेंकना चाहता हूं, मुझे यकीन है कि यह अधिकतर इस बात पर निर्भर करता है कि संकलक संदर्भों को कैसे लागू करता है, लेकिन जीसीसी के मामले में यह विचार है कि संदर्भ केवल स्टैक पर एक चर को इंगित कर सकता है वास्तव में सही नहीं है, उदाहरण के लिए इसे लें:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

यह कौन सा आउटपुट करता है:

THIS IS A STRING
0xbb2070 : 0xbb2070

यदि आप नोटिस करते हैं कि स्मृति पते बिल्कुल समान हैं, जिसका अर्थ है कि संदर्भ सफलतापूर्वक ढेर पर एक चर के लिए इंगित कर रहा है! अब अगर आप वास्तव में अजीब होना चाहते हैं, तो यह भी काम करता है:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

यह कौन सा आउटपुट करता है:

THIS IS A STRING

इसलिए एक संदर्भ हुड के नीचे एक सूचक है, वे दोनों सिर्फ एक स्मृति पता संग्रहीत कर रहे हैं, जहां पता इंगित कर रहा है अप्रासंगिक है, अगर आपको std :: cout << str_ref कहा जाता है तो आपको क्या लगता है; कॉलिंग और str_ref कॉल करने के बाद? खैर, जाहिर है, यह ठीक से संकलित करता है, लेकिन रनटाइम पर सेगमेंटेशन गलती का कारण बनता है क्योंकि यह अब एक वैध चर पर इंगित नहीं कर रहा है, हमारे पास अनिवार्य रूप से एक टूटा संदर्भ है जो अभी भी मौजूद है (जब तक यह गुंजाइश से बाहर न हो), लेकिन बेकार है।

दूसरे शब्दों में, एक संदर्भ कुछ भी नहीं है, जिसमें एक सूचक है जिसमें पॉइंटर यांत्रिकी का सार होता है, जिससे इसे सुरक्षित और उपयोग करना आसान हो जाता है (कोई आकस्मिक सूचक गणित नहीं, कोई मिश्रण नहीं होता है '।' और '->' इत्यादि), आपको लगता है उपरोक्त मेरे उदाहरणों की तरह किसी भी बकवास की कोशिश मत करो;)

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

संदर्भों के साथ याद रखने के लिए एकमात्र प्रमुख नियम यह है कि उन्हें घोषणा के समय परिभाषित किया जाना चाहिए (शीर्षलेख में किसी संदर्भ के अपवाद के साथ, उस स्थिति में इसे कन्स्ट्रक्टर में परिभाषित किया जाना चाहिए, उस वस्तु के बाद जिसमें यह निहित है इसे परिभाषित करने में बहुत देर हो चुकी है)।

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


यह प्रोग्राम प्रश्न के उत्तर को समझने में मदद कर सकता है। यह एक संदर्भ "जे" और एक सूचक "पीआरआर" का एक सरल कार्यक्रम है जो चर "x" को इंगित करता है।

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

कार्यक्रम चलाएं और आउटपुट पर एक नज़र डालें और आप समझेंगे।

इसके अलावा, 10 मिनट अतिरिक्त और इस वीडियो को देखें: https://www.youtube.com/watch?v=rlJrrGV0iOg


इससे कोई फ़र्क नहीं पड़ता कि यह कितनी जगह लेता है क्योंकि आप वास्तव में किसी भी पक्ष के प्रभाव (कोड निष्पादित किए बिना) नहीं देख सकते हैं।

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

उदाहरण के लिए:

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

प्रिंट करेगा:

in scope
scope_test done!

यह भाषा तंत्र है जो स्कोपगार्ड को काम करने की अनुमति देता है।


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

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 

एक अर्थपूर्ण अंतर है जो गूढ़ दिखाई दे सकता है यदि आप एक अमूर्त या यहां तक ​​कि अकादमिक फैशन में कंप्यूटर भाषाओं का अध्ययन करने से परिचित नहीं हैं।

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

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


अंतर यह है कि प्रोग्राम निष्पादन के दौरान कुछ समय में गैर-स्थिर पॉइंटर चर (निरंतर एक सूचक के साथ भ्रमित नहीं किया जा सकता है) को पॉइंटर अर्थशास्त्र का उपयोग करने के लिए (&, *) ऑपरेटरों की आवश्यकता होती है, जबकि संदर्भ प्रारंभिकरण पर सेट किए जा सकते हैं केवल (यही कारण है कि आप उन्हें केवल कन्स्ट्रक्टर प्रारंभकर्ता सूची में सेट कर सकते हैं, लेकिन किसी और तरह नहीं) और अर्थशास्त्र तक पहुंचने के सामान्य मूल्य का उपयोग करें। मूल रूप से संदर्भों को ऑपरेटर ओवरलोडिंग के समर्थन की अनुमति देने के लिए पेश किया गया था जैसा कि मैंने कुछ पुरानी किताब में पढ़ा था। जैसा कि किसी ने इस धागे में कहा है - पॉइंटर 0 पर सेट किया जा सकता है या जो भी वैल्यू आप चाहते हैं। 0 (नल, नलप्टर) का अर्थ है कि पॉइंटर को कुछ भी नहीं मिला है। यह शून्य सूचकांक dereference करने में एक त्रुटि है। लेकिन वास्तव में सूचक में एक मान हो सकता है जो कुछ सही स्मृति स्थान को इंगित नहीं करता है।उनके बदले में संदर्भ किसी उपयोगकर्ता को उस संदर्भ के बारे में संदर्भित करने की अनुमति नहीं देते हैं, जिसे इस तथ्य के कारण संदर्भित नहीं किया जा सकता है कि आप हमेशा सही प्रकार के रावल प्रदान करते हैं। यद्यपि रेफरेंस वैरिएबल को गलत मेमोरी लोकेशन में शुरू करने के कई तरीके हैं - लेकिन यह बेहतर है कि आप इस गहरे को विवरण में खोद न दें। मशीन स्तर पर दोनों पॉइंटर और संदर्भ कार्य समान रूप से - पॉइंटर्स के माध्यम से। आइए आवश्यक संदर्भों में वाक्य रचनात्मक चीनी हैं। रावल संदर्भ इस से अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं।यद्यपि रेफरेंस वैरिएबल को गलत मेमोरी लोकेशन में शुरू करने के कई तरीके हैं - लेकिन यह बेहतर है कि आप इस गहरे को विवरण में खोद न दें। मशीन स्तर पर दोनों पॉइंटर और संदर्भ कार्य समान रूप से - पॉइंटर्स के माध्यम से। आइए आवश्यक संदर्भों में वाक्य रचनात्मक चीनी हैं। रावल संदर्भ इस से अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं।यद्यपि रेफरेंस वैरिएबल को गलत मेमोरी लोकेशन में शुरू करने के कई तरीके हैं - लेकिन यह बेहतर है कि आप इस गहरे को विवरण में खोद न दें। मशीन स्तर पर दोनों पॉइंटर और संदर्भ कार्य समान रूप से - पॉइंटर्स के माध्यम से। आइए आवश्यक संदर्भों में वाक्य रचनात्मक चीनी हैं। रावल संदर्भ इस से अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं।


एक और अंतर यह है कि आप पॉइंटर्स को एक शून्य प्रकार (और इसका अर्थ है किसी भी चीज़ के लिए पॉइंटर) हो सकता है लेकिन शून्य के संदर्भ वर्जित हैं।

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

मैं नहीं कह सकता कि मैं इस विशेष अंतर से वास्तव में खुश हूं। मैं बहुत पसंद करता हूं कि किसी पते के साथ किसी भी संदर्भ के संदर्भ के संदर्भ में और अन्यथा संदर्भों के लिए समान व्यवहार की अनुमति दी जाएगी। यह संदर्भों का उपयोग कर memcpy जैसे सी पुस्तकालय कार्यों के कुछ समकक्षों को परिभाषित करने की अनुमति देगा।


मैं हमेशा this नियम से सी ++ कोर दिशानिर्देशों से निर्णय लेता हूं :

टी पर टी * पसंद करें और जब "कोई तर्क नहीं" एक वैध विकल्प है


संदर्भों का एक और दिलचस्प उपयोग उपयोगकर्ता द्वारा परिभाषित प्रकार की डिफ़ॉल्ट तर्क प्रदान करना है:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

डिफ़ॉल्ट स्वाद संदर्भों के अस्थायी 'पहलू के' बाइंड कॉन्स्ट संदर्भ 'का उपयोग करता है।


सी ++ में पॉइंटर का संदर्भ संभव है, लेकिन रिवर्स संभव नहीं है इसका मतलब है कि किसी संदर्भ के लिए पॉइंटर संभव नहीं है। सूचक के संदर्भ में पॉइंटर को संशोधित करने के लिए क्लीनर सिंटैक्स प्रदान करता है। इस उदाहरण को देखो:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

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

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

पॉइंटर के संदर्भ के बारे में अधिक जानकारी के लिए निम्नलिखित पर जाएं:

जैसा कि मैंने कहा, संदर्भ के लिए एक सूचक संभव नहीं है। निम्नलिखित प्रोग्राम आज़माएं:

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

मैं संदर्भों का उपयोग करता हूं जब तक कि मुझे इनमें से किसी एक की आवश्यकता न हो:

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

  • आप एक सूचक पर अंकगणित कर सकते हैं। उदाहरण के लिए,p += offset;


लोकप्रिय राय के विपरीत, एक संदर्भ होना संभव है जो पूर्ण है।

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

अनुमोदित, संदर्भ के साथ करना बहुत मुश्किल है - लेकिन यदि आप इसे प्रबंधित करते हैं, तो आप इसे ढूंढने की कोशिश कर अपने बालों को फाड़ देंगे। सी ++ में संदर्भ स्वाभाविक रूप से सुरक्षित नहीं हैं!

तकनीकी रूप से यह एक अमान्य संदर्भ है , न कि एक शून्य संदर्भ। सी ++ एक अवधारणा के रूप में शून्य संदर्भों का समर्थन नहीं करता है जैसा कि आप अन्य भाषाओं में पा सकते हैं। अन्य प्रकार के अमान्य संदर्भ भी हैं। कोई अमान्य संदर्भ एक अमान्य सूचक का उपयोग करने के रूप में, अपरिभाषित व्यवहार के दर्शक को बढ़ाता है।

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

ऊपर मेरा उदाहरण छोटा और contrved है। यहां एक और वास्तविक दुनिया का उदाहरण है।

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

मैं दोहराना चाहता हूं कि शून्य संदर्भ प्राप्त करने का एकमात्र तरीका विकृत कोड के माध्यम से है, और एक बार आपके पास यह अनिश्चित व्यवहार हो रहा है। एक शून्य संदर्भ की जांच करने के लिए यह कभी समझ में नहीं आता है; उदाहरण के लिए आप कोशिश कर सकते हैं if(&bar==NULL)... लेकिन संकलक अस्तित्व से बाहर कथन अनुकूलित कर सकता है! एक वैध संदर्भ कभी भी पूर्ण नहीं हो सकता है इसलिए संकलक के विचार से तुलना हमेशा झूठी होती है, और if धारा को मृत कोड के रूप में समाप्त करने के लिए स्वतंत्र है - यह अपरिभाषित व्यवहार का सार है।

परेशानी से बाहर रहने का सही तरीका एक संदर्भ बनाने के लिए एक पूर्ण सूचक को संदर्भित करना से बचाना है। इसे पूरा करने के लिए यहां एक स्वचालित तरीका है।

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

बेहतर लेखन कौशल वाले किसी व्यक्ति से इस समस्या को पुराने रूप से देखने के लिए, जिम हिस्लोप और हर्ब सटर से नल संदर्भ देखें।

एक नल पॉइंटर को डिफ्रेंस करने के खतरों के एक और उदाहरण के लिए रेमंड चेन द्वारा किसी अन्य प्लेटफ़ॉर्म पर पोर्ट कोड की कोशिश करते समय अपरिभाषित व्यवहार का खुलासा करना देखें।


सी ++ संदर्भ क्या है ( सी प्रोग्रामर के लिए )

स्वचालित संकेत के साथ एक संदर्भ को एक स्थिर सूचक (एक सूचक के साथ एक सूचक के साथ भ्रमित नहीं किया जाना चाहिए) के रूप में सोचा जा सकता है, यानी संकलक आपके लिए * ऑपरेटर लागू करेगा।

सभी संदर्भों को गैर-शून्य मान के साथ प्रारंभ किया जाना चाहिए या संकलन विफल हो जाएगा। किसी संदर्भ का पता प्राप्त करना न तो संभव है - पता ऑपरेटर इसके बजाय संदर्भित मान का पता वापस कर देगा - न ही संदर्भों पर अंकगणित करना संभव है।

सी प्रोग्रामर सी ++ संदर्भों को नापसंद कर सकते हैं क्योंकि यह तब स्पष्ट नहीं होगा जब संकेत होता है या यदि कोई तर्क मान द्वारा या सूचक हस्ताक्षर को देखे बिना पॉइंटर द्वारा पारित किया जाता है।

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

सी ++ एफएक्यू से निम्नलिखित कथन पर विचार करें:

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

लेकिन अगर कोई संदर्भ वास्तव में वस्तु था, तो संदर्भों को कैसे लटकाया जा सकता है? अप्रबंधित भाषाओं में, संदर्भों के लिए पॉइंटर्स की तुलना में किसी भी 'सुरक्षित' होने के लिए असंभव है - आमतौर पर केवल दायरे की सीमाओं के मूल्यों को विश्वसनीय रूप से करने का कोई तरीका नहीं है!

मैं सी ++ संदर्भों को उपयोगी क्यों मानता हूं

सी सी पृष्ठभूमि से आ रहा है, सी ++ संदर्भ कुछ हद तक मूर्खतापूर्ण अवधारणा की तरह लग सकते हैं, लेकिन जहां भी संभव हो वहां पॉइंटर्स की बजाय उन्हें अभी भी उपयोग करना चाहिए: स्वचालित संकेतक सुविधाजनक है, और RAII से निपटने के दौरान संदर्भ विशेष रूप से उपयोगी हो जाते हैं - लेकिन किसी भी कथित सुरक्षा के कारण नहीं लाभ, बल्कि इसलिए कि वे लेखन idiomatic कोड कम अजीब बनाते हैं।

आरएआईआई सी ++ की केंद्रीय अवधारणाओं में से एक है, लेकिन यह अर्थशास्त्र की प्रतिलिपि बनाने के साथ गैर-तुच्छ रूप से बातचीत करता है। संदर्भ द्वारा वस्तुओं को पास करने से इन मुद्दों से बचा जाता है क्योंकि कोई प्रतिलिपि शामिल नहीं होती है। यदि भाषा में संदर्भ मौजूद नहीं थे, तो आपको इसके बजाय पॉइंटर्स का उपयोग करना होगा, जो उपयोग करने के लिए अधिक बोझिल हैं, इस प्रकार भाषा डिजाइन सिद्धांत का उल्लंघन करते हैं कि सर्वोत्तम अभ्यास समाधान विकल्पों की तुलना में आसान होना चाहिए।


यह tutorial पर आधारित है । जो लिखा गया है वह इसे और स्पष्ट करता है:

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

बस याद रखने के लिए,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

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

निम्नलिखित कथन देखें,

int Tom(0);
int & alias_Tom = Tom;

alias_Tomएक के रूप में समझा जा सकता है alias of a variable(अलग है typedef, जो है alias of a type) Tom। इस तरह के बयान की शब्दावली को भूलना भी ठीक है, इसका संदर्भ बनाना है Tom


पॉइंटर्स और संदर्भों के बीच एक बहुत ही महत्वपूर्ण गैर-तकनीकी अंतर है: पॉइंटर द्वारा फ़ंक्शन में पारित एक तर्क गैर-कॉन्स्ट संदर्भ द्वारा किसी फ़ंक्शन को पास किए गए तर्क से कहीं अधिक दिखाई देता है। उदाहरण के लिए:

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

सी में वापस, एक कॉल जो दिखता है fn(x)केवल मूल्य से पारित किया जा सकता है, इसलिए यह निश्चित रूप से संशोधित नहीं कर सकता है x; एक तर्क को संशोधित करने के लिए आपको एक सूचक पास करने की आवश्यकता होगी fn(&x)। तो यदि किसी तर्क से पहले कोई तर्क नहीं &था, तो उसे पता चला कि इसे संशोधित नहीं किया जाएगा। (बातचीत, &मतलब संशोधित है, सच नहीं था क्योंकि आपको कभी-कभी constसूचक द्वारा बड़े पढ़ने-योग्य संरचनाएं पारित करनी पड़ती थीं ।)

कुछ लोग तर्क देते हैं कि कोड पढ़ने के दौरान यह एक उपयोगी सुविधा है, कि पॉइंटर पैरामीटर हमेशा गैर- constसंदर्भों के बजाय संशोधित पैरामीटर के लिए उपयोग किया जाना चाहिए , भले ही फ़ंक्शन कभी अपेक्षा न करे nullptr। यही है, वे लोग तर्क देते हैं कि fn3()ऊपर की तरह कार्य हस्ताक्षरों की अनुमति नहीं दी जानी चाहिए। Google का सी ++ शैली दिशानिर्देश इसका एक उदाहरण है।


आप सबसे महत्वपूर्ण हिस्सा भूल गए हैं:

पॉइंटर्स के साथ सदस्य-पहुंच का उपयोग करता है ->
संदर्भ उपयोग के साथ सदस्य-पहुंच .

foo.bar foo->bar से स्पष्ट रूप से बेहतर है जिस तरह से vi Emacs से स्पष्ट रूप से बेहतर है :-)


शायद कुछ रूपक मदद करेंगे; अपने डेस्कटॉप स्क्रीनस्पेस के संदर्भ में -

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






c++-faq