पॉइंटर वेरिएबल और C ++ में रेफरेंस वेरिएबल के बीच क्या अंतर हैं?


3261

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

लेकिन अंतर क्या हैं?


100
मुझे लगता है कि बिंदु 2 होना चाहिए "एक पॉइंटर को NULL होने की अनुमति है लेकिन एक संदर्भ नहीं है। केवल विकृत कोड ही NULL संदर्भ बना सकता है और इसका व्यवहार अपरिभाषित है।"
मार्क रैंसम

19
पॉइंटर्स केवल दूसरी प्रकार की वस्तु हैं, और C ++ में किसी भी ऑब्जेक्ट की तरह, वे एक चर हो सकते हैं। दूसरी ओर संदर्भ कभी भी वस्तु नहीं होते हैं, केवल चर होते हैं।
केरेक एसबी

19
यह चेतावनी के बिना संकलित करता है: int &x = *(int*)0;जीसीसी पर। संदर्भ वास्तव में NULL को इंगित कर सकता है।
कैलमेरियस

20
संदर्भ एक परिवर्तनशील उपनाम है
खालिद.क

20
मुझे पसंद है कि कैसे पहला वाक्य कुल गिरावट है। सन्दर्भों के अपने शब्दार्थ हैं।
को ऑर्बिट

जवाबों:


1705
  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 बाइट्स) होता है, जबकि एक संदर्भ एक ही मेमोरी एड्रेस (मूल चर के साथ) साझा करता है, लेकिन स्टैक पर कुछ जगह भी लेता है। चूँकि किसी संदर्भ का मूल चर के समान ही पता होता है, इसलिए संदर्भ को उसी चर के लिए किसी अन्य नाम के रूप में सोचना सुरक्षित होता है। नोट: एक पॉइंटर पॉइंट जो स्टैक या हीप पर हो सकता है। Ditto एक संदर्भ। इस कथन में मेरा दावा यह नहीं है कि एक सूचक को स्टैक की ओर इशारा करना चाहिए। एक पॉइंटर सिर्फ एक वैरिएबल है जो मेमोरी एड्रेस रखता है। यह चर स्टैक पर है। चूँकि स्टैक पर एक रेफरेंस का अपना स्पेस होता है, और चूंकि एड्रेस वैसा ही होता है जैसा कि वह वेरिएबल होता है। के बारे में अधिक ढेर ढेर बनाम। इसका मतलब यह है कि एक संदर्भ का वास्तविक पता है जो संकलक आपको नहीं बताएगा।

    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

    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. संकेत एक सरणी पर पुनरावृति कर सकते हैं; आप ++अगले आइटम पर जाने के लिए उपयोग कर सकते हैं जो एक संकेतक इंगित कर रहा है, और + 45 वें तत्व पर जाने के लिए। यह कोई बात नहीं है कि ऑब्जेक्ट किस आकार का है जो सूचक को इंगित करता है।

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

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

  8. कांस्टिट्यूशन अस्थायी लोगों के लिए बाध्य हो सकते हैं। संकेत (कुछ अप्रत्यक्ष के बिना नहीं) नहीं कर सकते हैं:

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

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


23
... लेकिन एनआरई अपरिभाषित है। उदाहरण के लिए, यदि कोई संदर्भ NULL (उदाहरण, और Ref == NULL) है तो आप परीक्षण नहीं कर सकते।
पाट नॉटज

69
नंबर 2 सच नहीं है। एक संदर्भ बस "एक ही चर के लिए दूसरा नाम नहीं है।" बिंदुओं के समान तरीके से, कार्यों को कक्षाओं में संग्रहित किया जा सकता है, आदि। वे स्वतंत्र रूप से मौजूद चर से इंगित करते हैं।
डेरेक पार्क

31
ब्रायन, स्टैक प्रासंगिक नहीं है। संदर्भ और संकेत को स्टैक पर स्थान नहीं लेना है। वे दोनों ढेर पर आवंटित किए जा सकते हैं।
डेरेक पार्क

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

38
एक और महत्वपूर्ण अंतर: संदर्भ एक सरणी में भरवां नहीं किया जा सकता
litb - Johannes Schaub

381

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

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

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

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

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

C ++ से निम्नलिखित कथन पर विचार करें :

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

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

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

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

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


17
@kriss: नहीं, आप संदर्भ द्वारा एक स्वचालित चर वापस करके एक लटकते हुए संदर्भ भी प्राप्त कर सकते हैं।
बेन वोइगट

12
@ क्रिश: सामान्य मामले में एक कंपाइलर का पता लगाना लगभग असंभव है। एक सदस्य फ़ंक्शन पर विचार करें जो एक वर्ग सदस्य चर का संदर्भ देता है: यह सुरक्षित है और संकलक द्वारा निषिद्ध नहीं होना चाहिए। फिर एक कॉलर जिसमें उस वर्ग का एक स्वचालित उदाहरण होता है, उस सदस्य को कॉल करता है, और संदर्भ देता है। प्रेस्टो: झूलने का संदर्भ। और हाँ, यह परेशानी पैदा करने वाली है, @kriss: यह मेरी बात है। कई लोग दावा करते हैं कि पॉइंटर्स पर संदर्भों का एक फायदा यह है कि संदर्भ हमेशा मान्य होते हैं, लेकिन ऐसा नहीं है।
बेन वोइगट

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

7
सन्दर्भ एक प्रकार का सूचक नहीं है। वे एक मौजूदा वस्तु का नया नाम हैं।
जूल

18
@ क्रैप्टिव: यदि आप भाषा के शब्दार्थ से चलते हैं, तो सच नहीं, यदि आप वास्तव में कार्यान्वयन को देखते हैं; C ++ एक अधिक 'जादुई' भाषा है जो C, और यदि आप संदर्भ से जादू को हटाते हैं, तो आप एक सूचक के साथ समाप्त होते हैं
क्रिस्टोफ

190

यदि आप वास्तव में पांडित्यपूर्ण होना चाहते हैं, तो एक बात है जिसे आप एक संदर्भ के साथ कर सकते हैं जो आप एक सूचक के साथ नहीं कर सकते हैं: एक अस्थायी वस्तु के जीवनकाल का विस्तार करें। C ++ में यदि आप किसी अस्थाई ऑब्जेक्ट के लिए एक कॉन्स्टेन्शन को बांधते हैं, तो उस ऑब्जेक्ट का जीवनकाल रेफरेंस का जीवनकाल बन जाता है।

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

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

यदि आप इसके बिना प्रयास करते हैं तो constइसे संकलित करने में विफल होना चाहिए। आप एक नॉन-कॉस्ट रेफरेंस को एक अस्थायी ऑब्जेक्ट से नहीं बांध सकते, न ही आप इस मामले के लिए उसका पता ले सकते हैं।


5
लेकिन इसके लिए उपयोग का मामला क्या है?
अहमद मुश्ताक

20
ठीक है, s3_copy एक अस्थायी निर्माण करेगा और फिर इसका निर्माण s3_copy में कर देगा, जबकि s3_reference सीधे अस्थायी का उपयोग करता है। फिर वास्तव में पांडित्यपूर्ण होने के लिए आपको रिटर्न वैल्यू ऑप्टिमाइज़ेशन को देखने की आवश्यकता होती है जिससे कंपाइलर को पहले मामले में कॉपी निर्माण को खत्म करने की अनुमति मिलती है।
मैट प्राइस

6
@digitalSurgeon: वहां का जादू काफी शक्तिशाली है। ऑब्जेक्ट जीवनकाल को const &बाइंडिंग के तथ्य द्वारा विस्तारित किया जाता है , और केवल जब संदर्भ दायरे से बाहर चला जाता है तो वास्तविक संदर्भित प्रकार के विध्वंसक (संदर्भ प्रकार की तुलना में, जो आधार हो सकता है) कहा जाता है। चूंकि यह एक संदर्भ है, इसके बीच में कोई स्लाइसिंग नहीं होगी।
डेविड रॉड्रिग्ज - dribeas

9
C ++ 11 के लिए अद्यतन: अंतिम वाक्य को पढ़ना चाहिए "आप एक अस्थायी रूप से एक गैर-कॉन्स्टल लैवल्यू संदर्भ को बाँध नहीं सकते हैं " क्योंकि आप एक नॉन-कॉस्ट रेवल्यू रेफरेंस को अस्थायी रूप से बाँध सकते हैं , और इसमें एक ही जीवनकाल-विस्तारित व्यवहार है।
ओकटालिस्ट

4
@ अहम्मुष्टक: इसका प्रमुख उपयोग व्युत्पन्न वर्ग है । यदि कोई विरासत शामिल नहीं है, तो आप मूल्य शब्दार्थ का उपयोग कर सकते हैं, जो आरवीओ / चाल निर्माण के कारण सस्ता या मुफ्त होगा। लेकिन अगर आपके पास है Animal x = fast ? getHare() : getTortoise()तो xक्लासिक स्लाइसिंग समस्या का सामना करना पड़ेगा, जबकि Animal& x = ...सही तरीके से काम करेगा।
आर्थर टाका

127

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

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

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

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

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


8
मुझे लगता है कि यह अब तक का सबसे अच्छा जवाब है। अन्य लोग संदर्भ और संकेत के बारे में बात करते हैं जैसे कि वे अलग-अलग जानवर हैं और फिर बताते हैं कि वे व्यवहार में कैसे भिन्न हैं। यह किसी भी आसान imho बातें नहीं करता है। मैंने हमेशा T* constअलग-अलग वाक्य रचना चीनी के साथ संदर्भों को समझा है (जो आपके कोड से बहुत * और & को खत्म करने के लिए होता है)।
कार्लो वुड

2
"एक कास्ट पॉइंटर का लक्ष्य इसका पता लेने और एक कास्ट कास्ट का उपयोग करके बदला जा सकता है।" ऐसा करना अपरिभाषित व्यवहार है। देखें stackoverflow.com/questions/25209838/... जानकारी के लिए।
18'18

1
किसी संदर्भ के संदर्भ या कॉन्स्टेंट पॉइंटर (या किसी भी कास्ट स्केलर) के मान को बदलने की कोशिश करना समानता अवैध है। आप क्या कर सकते हैं: अंतर्निहित रूपांतरण द्वारा जोड़ा गया एक कास्ट योग्यता को हटा दें: int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci);ठीक है।
जिज्ञासु

1
यहाँ अंतर यूबी बनाम शाब्दिक रूप से असंभव है। C ++ में कोई सिंटैक्स नहीं है जो आपको संदर्भ बिंदुओं को बदलने देगा।

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

125

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

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

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

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

वास्तविक त्रुटि, NULL पॉइंटर की डेरेफ्रेंसिंग में है, एक संदर्भ में असाइनमेंट से पहले। लेकिन मुझे ऐसे किसी भी कंपाइलर के बारे में पता नहीं है जो उस स्थिति में कोई त्रुटि उत्पन्न करेगा - त्रुटि कोड में एक बिंदु और आगे तक फैलती है। यही बात इस समस्या को इतना कपटी बना देती है। अधिकांश समय, यदि आप एक NULL पॉइंटर को रोकते हैं, तो आप उस स्थान पर सही क्रैश करते हैं और यह पता लगाने के लिए बहुत डीबगिंग नहीं लेता है।

ऊपर दिया गया मेरा उदाहरण छोटा और आकस्मिक है। यहाँ एक और अधिक वास्तविक दुनिया उदाहरण है।

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क्लॉज़ को मृत कोड के रूप में समाप्त करने के लिए स्वतंत्र है - यह अपरिभाषित व्यवहार का सार है।

मुसीबत से बाहर रहने का उचित तरीका यह है कि एक संदर्भ बनाने के लिए NULL पॉइंटर को डीफ़र करने से बचें। इसे पूरा करने के लिए यहां एक स्वचालित तरीका है।

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

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

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


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

9
निशान का एक वैध तर्क है। यह तर्क कि एक सूचक NULL हो सकता है और आपको जांचना होगा कि यह वास्तविक नहीं है: यदि आप कहते हैं कि किसी फ़ंक्शन को गैर-NULL की आवश्यकता है, तो कॉल करने वाले को ऐसा करना होगा। इसलिए यदि कॉलर नहीं करता है तो वह अपरिभाषित व्यवहार कर रहा है। जैसे निशान बुरा संदर्भ के साथ किया था
litb - Johannes Schaub

13
विवरण गलत है। यह कोड एक संदर्भ हो सकता है या नहीं जो NULL हो। इसका व्यवहार अपरिभाषित है। यह पूरी तरह से वैध संदर्भ बना सकता है। यह बिल्कुल भी संदर्भ बनाने में विफल हो सकता है।
डेविड श्वार्ट्ज

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

6
एक अशक्त सूचक को गलत ठहराना गलत है। कोई भी प्रोग्राम जो किसी संदर्भ को आरंभ करने के लिए भी गलत है। यदि आप एक पॉइंटर से एक संदर्भ को इनिशियलाइज़ कर रहे हैं, तो आपको हमेशा यह देखना चाहिए कि पॉइंटर वैध है। यहां तक ​​कि अगर यह सफल होता है तो अंतर्निहित वस्तु को किसी भी समय गैर-मौजूदा वस्तु, सही के संदर्भ के लिए छोड़ दिया जा सकता है? आप जो कह रहे हैं वह अच्छी चीज है। मुझे लगता है कि असली मुद्दा यह है कि संदर्भ को "अशक्तता" के लिए जांचने की आवश्यकता नहीं है जब आप देखते हैं कि एक और पॉइंटर न्यूनतम, मुखर होना चाहिए।
t0rakka

114

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

सदस्य-अभिगम बिंदुओं के ->
साथ संदर्भ-उपयोग के साथ सदस्य-अभिगम का उपयोग करता है.

foo.barहै स्पष्ट रूप से बेहतर करने के लिए foo->barएक ही रास्ता है कि में vi है स्पष्ट रूप से बेहतर करने के लिए Emacs :-)


4
@ ओरियन एडवर्ड्स> बिंदुओं के साथ सदस्य का उपयोग ->> संदर्भ उपयोगों के साथ सदस्य का उपयोग। यह 100% सच नहीं है। आपके पास एक पॉइंटर का संदर्भ हो सकता है। इस मामले में, आप का उपयोग करते हुए डी-रेफर किए गए पॉइंटर के सदस्यों तक पहुंचेंगे -> संरचनात्मक नोड {नोड * अगला; }; नोड * पहले; // p एक पॉइंटर शून्य foo (Node * & p) {p-> अगला = पहला है; } नोड * बार = नया नोड; foo (बार); - ओपी: क्या आप rvalues ​​और lvalues ​​की अवधारणाओं से परिचित हैं?

3
स्मार्ट पॉइंटर्स दोनों हैं। (स्मार्ट पॉइंटर क्लास पर तरीके) और -> (अंतर्निहित प्रकार पर तरीके)।
JBRWilkinson

1
@ user6105 ओरियन एडवर्ड्स का कथन वास्तव में 100% सत्य है। "[] द-रेफ़र्ड पॉइंटर के एक्सेस सदस्यों" एक पॉइंटर में कोई सदस्य नहीं है। पॉइंटर जिस ऑब्जेक्ट को संदर्भित करता है, उसमें सदस्य होते हैं, और उन तक पहुंच ठीक उसी तरह होती है जो ->पॉइंटर्स के संदर्भ में प्रदान करता है, जैसे कि पॉइंटर के साथ ही।
मैक्स Truxa

1
ऐसा क्यों है .और ->vi और emacs के साथ कुछ करना है :)
artm

10
@artM - यह एक मजाक था, और शायद गैर-देशी अंग्रेजी बोलने वालों के लिए कोई मतलब नहीं है। मैं क्षमाप्रार्थी हूं। यह समझाने के लिए कि क्या वीआईए एमएसीएस से बेहतर है, पूरी तरह से व्यक्तिपरक है। कुछ लोग सोचते हैं कि vi बहुत बेहतर है, और अन्य लोग इसके ठीक विपरीत सोचते हैं। इसी तरह, मुझे लगता है कि उपयोग .करना बेहतर है ->, लेकिन vi बनाम emacs की तरह, यह पूरी तरह से व्यक्तिपरक है और आप कुछ भी साबित नहीं कर सकते हैं
ओरियन एडवर्ड्स

73

संदर्भ बहुत हद तक संकेत के समान हैं, लेकिन वे विशेष रूप से संकलित करने में सहायक होने के लिए तैयार किए जाते हैं।

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

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

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

एक अनुकूलन करने वाले कंपाइलर को एहसास हो सकता है कि हम एक [0] और [1] काफी गुच्छा बना रहे हैं। यह एल्गोरिथम को ऑप्टिमाइज़ करना पसंद करेगा:

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

इस तरह का अनुकूलन करने के लिए, यह साबित करने की जरूरत है कि कॉल के दौरान कुछ भी नहीं बदल सकता है [1]। यह करना आसान है। मैं कभी भी 2 से कम नहीं होता, इसलिए सरणी [i] कभी भी सरणी [1] को संदर्भित नहीं कर सकता। हो सकता हैModify () को संदर्भ के रूप में a0 दिया गया हो (उपनाम सरणी [0])। क्योंकि कोई "संदर्भ" अंकगणित नहीं है, इसलिए संकलक को यह साबित करना होगा कि शायदModify को कभी भी x का पता नहीं मिलता है, और यह साबित हो चुका है कि कुछ भी नहीं बदलता है [1]।

यह भी साबित करना है कि भविष्य में कॉल को पढ़ने / लिखने के लिए कोई तरीका नहीं है [0], जबकि हमारे पास a0 में इसकी एक अस्थायी रजिस्टर कॉपी है। यह साबित करने के लिए अक्सर तुच्छ होता है, क्योंकि कई मामलों में यह स्पष्ट है कि संदर्भ कभी भी एक श्रेणी के उदाहरण की तरह एक स्थायी संरचना में संग्रहीत नहीं किया जाता है।

अब यही बात पॉइंटर्स के साथ भी करें

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

व्यवहार वही है; केवल अब यह साबित करना बहुत कठिन है कि हो सकता है किModify कभी भी सरणी को संशोधित न करे [1], क्योंकि हमने पहले ही इसे पॉइंटर दे दिया था; बिल्ली थैले से बाहर है। अब इसे और अधिक कठिन प्रमाण देना है: शायद यह साबित करने के लिए कि शायद यह कभी नहीं लिखता & # 1 के लिए एक स्थिर विश्लेषण। यह भी साबित करना है कि यह एक पॉइंटर को बचाता है जो सरणी [0] को संदर्भित कर सकता है, जो कि सिर्फ मुश्किल के रूप में।

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

बेशक, इस तरह के चतुर अनुकूलन को छोड़कर, संकलक वास्तव में जरूरत पड़ने पर संदर्भों में बदल जाएंगे।

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

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

आम तौर पर अस्थायी वस्तुओं जैसे कि कॉल द्वारा बनाई गई createF(5)अभिव्यक्ति के अंत में नष्ट हो जाती हैं। हालाँकि, उस ऑब्जेक्ट को एक संदर्भ में बांधने से ref, C ++ उस अस्थायी ऑब्जेक्ट के जीवन काल का विस्तार करेगा जब तक कि वह refदायरे से बाहर न हो जाए।


सच है, शरीर को दिखाई पड़ता है। हालांकि, यह निर्धारित करना कि maybeModifyसंबंधित किसी भी चीज का पता नहीं लेता है , यह xसाबित करने की तुलना में काफी आसान है कि सूचक अंकगणित का एक गुच्छा उत्पन्न नहीं होता है।
Cort Ammon

मेरा मानना ​​है कि ऑप्टिमाइज़र पहले से ही ऐसा करता है कि "पॉइंटर एरीथेमेटिक का एक गुच्छा नहीं होता है" अन्य कारणों के एक गुच्छा के लिए जांच करें।
बेन वोयगट

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

मुझे लगता है कि किसी ने गलत तरीके से एक टिप्पणी के रूप में अप्रचलित के रूप में झंडी दिखाई void maybeModify(int& x) { 1[&x]++; }, जिसके ऊपर अन्य टिप्पणियां चर्चा कर रही हैं
बेन Voigt

67

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

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

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

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

अब असली कोड की कुछ व्याख्या:

int a = 0;
int& b = a;

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

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

int a;
increment(a);

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

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


2
एक संदर्भ एल-मान का एक संदर्भ है, एक चर के लिए जरूरी नहीं है। इस वजह से, यह एक वास्तविक उर्फ ​​(एक संकलन-समय निर्माण) की तुलना में एक संकेतक के बहुत करीब है। जिन भावों को संदर्भित किया जा सकता है, वे हैं * p या सम * p ++

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

1
@VincentRobert: यह एक पॉइंटर के समान कार्य करेगा ... यदि फ़ंक्शन इनलाइन है, तो संदर्भ और पॉइंटर दोनों को अनुकूलित किया जाएगा। यदि कोई फ़ंक्शन कॉल है, तो ऑब्जेक्ट का पता फ़ंक्शन को पास करना होगा।
बेन Voigt

1
int * p = NULL; int & r = * p; NULL को इंगित करने वाला संदर्भ; अगर (आर) {} -> उछाल;)
श्री

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

44

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


10
एक काउंटर-उदाहरण के लिए मार्क रैनसम का जवाब देखें। यह संदर्भों के बारे में सबसे अक्सर मुखर मिथक है, लेकिन यह एक मिथक है। मानक द्वारा आपके पास एकमात्र गारंटी यह है कि आपके पास NULL संदर्भ होने पर तुरंत UB हो। लेकिन यह कहने के लिए कि "यह कार सुरक्षित है, यह कभी भी सड़क से नहीं उतर सकती। (हम किसी भी तरह की ज़िम्मेदारी नहीं लेते हैं, अगर आप इसे सड़क से दूर कर देते हैं, तो बस विस्फोट हो सकता है।"
cmaster -

17
@cmaster: एक वैध कार्यक्रम में , एक संदर्भ शून्य नहीं हो सकता। लेकिन एक सूचक कर सकता है। यह कोई मिथक नहीं है, यह एक सच्चाई है।
user541686

8
@ मेहरदाद हाँ, वैध कार्यक्रम सड़क पर रहते हैं। लेकिन आपके कार्यक्रम को वास्तव में लागू करने के लिए कोई यातायात बाधा नहीं है। सड़क के बड़े हिस्से वास्तव में लापता निशान हैं। इसलिए रात में सड़क पर उतरना बेहद आसान है। और ऐसे बग्स को डिबग करने के लिए यह महत्वपूर्ण है कि आप जानते हैं कि यह हो सकता है: अशक्त संदर्भ आपके प्रोग्राम को क्रैश करने से पहले प्रचार कर सकता है, ठीक उसी तरह जैसे कि एक नल पॉइंटर कर सकता है। और जब आपके पास void Foo::bar() { virtual_baz(); }उस segfaults जैसा कोड होता है। यदि आप इस बात से अवगत नहीं हैं कि संदर्भ शून्य हो सकते हैं, तो आप अशक्त को उसके मूल में वापस नहीं ले जा सकते।
सेंटास्टर - मोनिका

4
int * p = NULL; int & r = * p; NULL को इंगित करने वाला संदर्भ; अगर (आर) {} -> उछाल;) -
श्री

10
@ श्री int &r=*p;अपरिभाषित व्यवहार है। उस बिंदु पर, यदि आप एक नहीं है "शून्य के संदर्भ की ओर इशारा करते," यदि आप एक प्रोग्राम है जो है नहीं रह गया है के बारे में समझाया जा सकता है सब पर
cdhowie

34

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

इन दो कार्यक्रम के टुकड़े पर विचार करें। पहले में, हम एक पॉइंटर को दूसरे को असाइन करते हैं:

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

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

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

यह असाइनमेंट ival को बदल देता है, आरआई द्वारा संदर्भित मूल्य, और संदर्भ ही नहीं। असाइनमेंट के बाद, दो संदर्भ अभी भी अपनी मूल वस्तुओं को संदर्भित करते हैं, और उन वस्तुओं का मूल्य अब भी समान है।


"एक संदर्भ हमेशा एक वस्तु को संदर्भित करता है" बस पूरी तरह से झूठ है
बेन Voigt

31

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

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

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


26

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

    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. 

19

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

दूसरी ओर, संदर्भ और संकेत के बीच एक बड़ा अंतर यह है कि अस्थायी संदर्भों को सौंपा गया संदर्भ तब तक जीवित रहता है जब तक कि संदर्भ संदर्भ से बाहर नहीं हो जाता है।

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

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!

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


1
आप संदर्भ का पता नहीं लगा सकते हैं, लेकिन इसका मतलब यह नहीं है कि वे भौतिक रूप से जगह नहीं लेते हैं। अनुकूलन को छोड़कर, वे सबसे निश्चित रूप से कर सकते हैं।
लाइटवेट दौड़ ऑर्बिट

2
प्रभाव के बावजूद, "स्टैक पर एक संदर्भ किसी भी स्थान को बिल्कुल नहीं लेता है" वर्तमान में गलत है।
को ऑर्बिट

1
@ टोमलाक, ठीक है, जो संकलक पर भी निर्भर करता है। लेकिन हां, यह कहना थोड़ा गड़बड़ है। मुझे लगता है कि यह सिर्फ हटाने के लिए कम भ्रामक होगा।
एमएसएन

1
किसी भी विशिष्ट मामले में यह हो सकता है या नहीं। तो "यह नहीं करता है" एक स्पष्ट दावे के रूप में गलत है। मैं भी येही कह रहा हूँ। :) [मुझे याद नहीं है कि इस मुद्दे पर मानक क्या कहते हैं; संदर्भ सदस्यों के नियम "संदर्भों में स्थान ले सकते हैं" के एक सामान्य नियम को लागू कर सकते हैं, लेकिन मेरे पास मेरे साथ मानक की प्रति यहां समुद्र तट पर नहीं है: डी]
ऑर्बिट में लाइटनेस दौड़

19

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

>>> 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


1
और यदि किसी वर्ग में एक संदर्भ चर है, तो इसे प्रारंभिक सूची में एक nullptr या एक मान्य ऑब्जेक्ट के साथ आरंभीकृत किया जाना चाहिए।
दुष्कर्म

1
इस उत्तर में शब्द का प्रयोग बहुत अधिक वास्तविक उपयोग के लिए भ्रमित करने वाला है। इसके अलावा, @Misgevolution, क्या आप गंभीरता से पाठकों के लिए एक संदर्भ के साथ एक आरंभ करने की सिफारिश कर रहे हैं nullptr? क्या आपने वास्तव में इस धागे का कोई अन्य हिस्सा पढ़ा है, या ...?
अंडरस्कोर_ड

1
मेरी बुरी, उस बेवकूफी भरी बात के लिए मुझे खेद है। मुझे उस समय तक नींद से वंचित होना पड़ा होगा। 'nullptr के साथ आरंभ करना' पूरी तरह से गलत है।
मिस्गेवोल्यूशन

18

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

int& j = i;

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

int* const j = &i;

13
यह वह नहीं है जो सी ++ मानक कहता है, और यह कंपाइलर को आपके उत्तर द्वारा वर्णित तरीके से संदर्भों को लागू करने के लिए आवश्यक नहीं है।
जोगोजपन

@ जोगोजपन: एक संदर्भ को लागू करने के लिए C ++ कंपाइलर के लिए मान्य कोई भी तरीका इसके लिए एक constपॉइंटर को लागू करने का एक वैध तरीका है । यह लचीलापन साबित नहीं करता है कि एक संदर्भ और एक सूचक के बीच अंतर है।
बेन वोइगेट

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

एक संदर्भ है एक वस्तु के लिए दिया एक और नाम। कंपाइलर को किसी भी प्रकार के कार्यान्वयन की अनुमति है, जब तक आप अंतर नहीं बता सकते हैं, इसे "as-if" नियम के रूप में जाना जाता है। यहाँ महत्वपूर्ण हिस्सा यह है कि आप अंतर नहीं बता सकते। यदि आप पता लगा सकते हैं कि एक पॉइंटर में स्टोरेज नहीं है, तो कंपाइलर गलती में है। यदि आप जान सकते हैं कि एक संदर्भ में भंडारण नहीं है, तो कंपाइलर अभी भी अनुरूप है।
sp2danny

17

सीधा जवाब

C ++ में एक संदर्भ क्या है? प्रकार के कुछ विशिष्ट उदाहरण जो ऑब्जेक्ट प्रकार नहीं है

C ++ में पॉइंटर क्या है? प्रकार के कुछ विशिष्ट उदाहरण जो एक वस्तु प्रकार है

से ऑब्जेक्ट प्रकार की आईएसओ सी ++ परिभाषा :

एक वस्तु प्रकार (संभवत: है सीवी -qualified) प्रकार है कि एक समारोह का प्रकार, नहीं एक संदर्भ प्रकार, और नहीं नहीं है सीवी शून्य।

यह जानना महत्वपूर्ण हो सकता है, ऑब्जेक्ट प्रकार C ++ में ब्रह्मांड के शीर्ष-स्तरीय श्रेणी है। संदर्भ भी एक शीर्ष-स्तरीय श्रेणी है। लेकिन पॉइंटर नहीं है।

यौगिक प्रकार के संदर्भ में बिंदुओं और संदर्भों का एक साथ उल्लेख किया गया है । यह मूल रूप से घोषित (और विस्तारित) सी से विरासत में मिलाया गया सिंटैक्स की प्रकृति के कारण है, जिसका कोई संदर्भ नहीं है। (इसके अलावा, C ++ 11 के बाद से एक से अधिक प्रकार के संदर्भों के घोषणाकर्ता हैं, जबकि पॉइंटर्स अभी भी "unityped:: &+ &&बनाम *।) हैं, इसलिए इस संदर्भ में C की समान शैली के साथ" एक्सटेंशन "द्वारा विशिष्ट भाषा का मसौदा तैयार करना कुछ हद तक उचित है। । (मैं अभी भी तर्क दूंगा कि घोषणाकर्ताओं का वाक्यविन्यास वाक्य-विन्यास की व्यर्थता को बहुत व्यर्थ कर देता है, मानव उपयोगकर्ताओं और कार्यान्वयन दोनों को निराश करता है। इस प्रकार, वे सभी अंतर्निहित होने के योग्य नहीं हैं एक नई भाषा डिजाइन में। यह पीएल डिजाइन के बारे में एक पूरी तरह से अलग विषय है, हालांकि।)

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

ऊपर दिए गए कथनों को केवल "संकेत" और प्रकार के रूप में "संदर्भ" का उल्लेख करें। उनके उदाहरणों (जैसे चर) के बारे में कुछ दिलचस्पी सवाल हैं। वहाँ भी कई भ्रांतियाँ आती हैं।

शीर्ष-स्तरीय श्रेणियों के अंतर पहले से ही कई ठोस अंतरों को प्रकट कर सकते हैं जो सीधे संकेत करने के लिए बंधे नहीं हैं:

  • ऑब्जेक्ट प्रकारों में शीर्ष-स्तरीय cvक्वालिफायर हो सकते हैं । सन्दर्भ नहीं दे सकते।
  • अमूर्त मशीन शब्दार्थ के अनुसार वस्तु प्रकारों का परिवर्तन भंडारण पर कब्जा कर लेता है । संदर्भ आवश्यक कब्जे के भंडारण नहीं है (विवरण के लिए नीचे दी गई गलतफहमी के बारे में अनुभाग देखें)।
  • ...

संदर्भों पर कुछ और विशेष नियम:

  • मिश्रित घोषणाकर्ता संदर्भों पर अधिक प्रतिबंधात्मक हैं।
  • सन्दर्भ ढह सकते हैं ।
    • &&टेम्पलेट पैरामीटर कटौती के दौरान संदर्भ ढहने के आधार पर मापदंडों पर विशेष नियम ("अग्रेषण संदर्भ" के रूप में) मापदंडों के "पूर्ण अग्रेषण" की अनुमति देते हैं ।
  • प्रारंभ में संदर्भों के विशेष नियम हैं। एक संदर्भ प्रकार के रूप में घोषित चर का जीवनकाल विस्तार के माध्यम से सामान्य वस्तुओं के लिए भिन्न हो सकता है।
    • BTW, इनिशियलाइज़ेशन जैसे कुछ अन्य std::initializer_listसंदर्भ संदर्भ आजीवन विस्तार के कुछ समान नियमों का पालन करते हैं। यह कीड़े का दूसरा तरीका है।
  • ...

भ्रांतियां हैं

सिंथेटिक चीनी

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

तकनीकी रूप से, यह सादा गलत है। सन्दर्भ C ++ में किसी भी अन्य विशेषताओं का वाक्यगत शर्करा नहीं है, क्योंकि वे किसी भी अर्थ भिन्नता के बिना अन्य विशेषताओं द्वारा बिल्कुल प्रतिस्थापित नहीं किए जा सकते हैं।

(इसी तरह, लंबोदर-एक्सप्रेशन एस C ++ में किसी भी अन्य विशेषताओं की सिंथैटिक शुगर नहीं है क्योंकि इसे कैप्चर किए गए वेरिएबल्स के डिक्लेरेशन ऑर्डर की तरह "अनिर्दिष्ट" गुणों के साथ सटीक रूप से सिम्युलेटेड नहीं किया जा सकता है , जो महत्वपूर्ण हो सकता है क्योंकि ऐसे वेरिएबल्स का इनिशियलाइज़ेशन ऑर्डर हो सकता है महत्वपूर्ण।)

इस सख्त अर्थ में C ++ में केवल कुछ प्रकार के सिंथैटिक शर्करा होते हैं। एक उदाहरण है (C से विरासत में मिला) बिल्ट-इन (नॉन-ओवरलोडेड) ऑपरेटर [], जिसे बिल्ट-इन ऑपरेटर यूनरी *और बाइनरी पर संयोजन के विशिष्ट रूपों के समान अर्थ गुण होते हैं+

भंडारण

तो, एक पॉइंटर और एक संदर्भ दोनों मेमोरी की समान मात्रा का उपयोग करते हैं।

ऊपर बयान केवल गलत है। इस तरह की भ्रांतियों से बचने के लिए ISO C ++ नियम देखें।

से [intro.object] / 1 :

... एक वस्तु अपने पूरे जीवनकाल में, और विनाश की अपनी अवधि में, निर्माण की अवधि में भंडारण के एक क्षेत्र पर कब्जा कर लेती है। ...

से [dcl.ref] / 4 :

यह अनिर्दिष्ट है कि किसी संदर्भ में भंडारण की आवश्यकता है या नहीं।

ध्यान दें कि ये शब्दार्थ गुण हैं।

उपयोगितावाद

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

लेकिन कहानी यहीं पर खत्म नहीं हो जाती। मेरा मतलब है कि बिंदुओं बनाम संदर्भों की तुलना में अधिक चीजें हैं जिन्हें आपको विचार करना होगा।

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

  • कभी-कभी भाषा नियमों को स्पष्ट रूप से विशिष्ट प्रकारों का उपयोग करने की आवश्यकता होती है। यदि आप इन सुविधाओं का उपयोग करना चाहते हैं, तो नियमों का पालन करें।
    • कॉपी कंस्ट्रक्टरों को विशिष्ट प्रकार के cv - &संदर्भ प्रकार की आवश्यकता होती है जैसे कि 1 पैरामीटर प्रकार। (और आमतौर पर यह constयोग्य होना चाहिए ।)
    • मूव कन्स्ट्रक्टर्स को विशिष्ट प्रकार के cv - &&रेफरेंस टाइप की आवश्यकता होती है, जैसे 1 पैरामीटर टाइप। (और आमतौर पर कोई क्वालिफायर नहीं होना चाहिए।)
    • ऑपरेटरों के विशिष्ट अधिभार को संदर्भ या गैर संदर्भ प्रकार की आवश्यकता होती है। उदाहरण के लिए:
      • operator=विशेष सदस्य कार्यों के रूप में अतिभारित करने के लिए प्रतिलिपि / चाल बिल्डरों के 1 पैरामीटर के समान संदर्भ प्रकारों की आवश्यकता होती है।
      • पोस्टफ़िक्स ++को डमी की आवश्यकता होती है int
      • ...
  • यदि आप जानते हैं कि पास-दर-मूल्य (अर्थात गैर-संदर्भ प्रकारों का उपयोग करना) पर्याप्त है, तो इसे सीधे उपयोग करें, खासकर जब C ++ 17 अनिवार्य कॉपी एलिसन का समर्थन करने वाले कार्यान्वयन का उपयोग कर रहे हों। ( चेतावनी : हालांकि, आवश्यकता के बारे में पूरी तरह से कारण बहुत जटिल हो सकता है ।)
  • आप स्वामित्व के साथ कुछ हैंडल संचालित करने के लिए चाहते हैं, स्मार्ट संकेत की तरह उपयोग करें unique_ptrऔर shared_ptr(या यहां तक कि अपने आप को द्वारा homebrew लोगों के साथ अगर आप उन्हें आवश्यकता होने के लिए अपारदर्शी , बल्कि कच्चे संकेत से)।
  • यदि आप किसी श्रेणी पर कुछ पुनरावृत्तियाँ कर रहे हैं, तो पुनरावृत्तियों का उपयोग करें (या कुछ श्रेणियां जो अभी तक मानक लाइब्रेरी द्वारा प्रदान नहीं की गई हैं), कच्चे पॉइंटर्स के बजाय जब तक आप आश्वस्त नहीं होते हैं कि कच्चे पॉइंटर्स बेहतर करेंगे (जैसे कम हेडर निर्भरता के लिए) बहुत विशिष्ट मामलों।
  • यदि आप जानते हैं कि पास-दर-मूल्य पर्याप्त है और आप कुछ स्पष्ट अशोभनीय शब्दार्थ चाहते हैं std::optional, तो कच्चे पॉइंटर्स के बजाय रैपर का उपयोग करें ।
  • यदि आप जानते हैं कि पास-दर-मूल्य ऊपर दिए गए कारणों के लिए आदर्श नहीं है, और आप अशक्त शब्दार्थ नहीं चाहते हैं, तो {lvalue, rvalue, अग्रेषण} -references का उपयोग करें।
  • यहां तक ​​कि जब आप पारंपरिक पॉइंटर जैसे शब्दार्थ चाहते हैं, तो अक्सर कुछ अधिक उपयुक्त होते हैं, जैसे observer_ptrलाइब्रेरी फंडामेंटल टीएस।

वर्तमान भाषा में केवल अपवादों पर काम नहीं किया जा सकता है:

  • जब आप ऊपर स्मार्ट पॉइंटर्स लागू कर रहे हैं, तो आपको कच्चे पॉइंटर्स से निपटना पड़ सकता है।
  • विशिष्ट भाषा-अंतरायन दिनचर्या के लिए पॉइंटर्स की आवश्यकता होती है, जैसे operator new। (हालांकि, cv - void*अभी भी सामान्य ऑब्जेक्ट पॉइंटर्स की तुलना में काफी अलग और सुरक्षित है क्योंकि यह अप्रत्याशित पॉइंटर अंकगणित को नियंत्रित करता है जब तक कि आप void*GNU जैसे कुछ गैर-अनुरूपता विस्तार पर भरोसा नहीं कर रहे हैं ।)
  • फ़ंक्शन पॉइंटर्स को कैद किए बिना लैम्ब्डा एक्सप्रेशन से परिवर्तित किया जा सकता है, जबकि फ़ंक्शन संदर्भ नहीं कर सकते। आपको ऐसे मामलों के लिए गैर-सामान्य कोड में फ़ंक्शन पॉइंटर्स का उपयोग करना होगा, यहां तक ​​कि आप जानबूझकर अशक्त मूल्यों को नहीं चाहते हैं।

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

  • आपको पुराने-शैली (C) API की सेवा देनी होगी।
  • आपको विशिष्ट C ++ कार्यान्वयन की ABI आवश्यकताओं को पूरा करना होगा।
  • आपको विशिष्ट कार्यान्वयन की मान्यताओं के आधार पर विभिन्न भाषा कार्यान्वयनों (विभिन्न विधानसभाओं, भाषा रनटाइम और कुछ उच्च-स्तरीय क्लाइंट भाषाओं के एफएफआई सहित) के साथ रनटाइम पर इंटरऑपरेट करना होगा।
  • आपको कुछ चरम मामलों में अनुवाद (संकलन और लिंकिंग) की दक्षता में सुधार करना होगा।
  • आपको कुछ चरम मामलों में प्रतीक ब्लोट से बचना होगा।

भाषा तटस्थता कैविएट

यदि आप कुछ Google खोज परिणाम (C ++ के लिए विशिष्ट नहीं) के माध्यम से प्रश्न देखते हैं , तो यह गलत जगह होने की संभावना है।

C ++ में सन्दर्भ काफी "विषम" है, क्योंकि यह अनिवार्य रूप से प्रथम श्रेणी का नहीं है: उन्हें वस्तुओं के रूप में माना जाएगा या जिन कार्यों को संदर्भित किया जा रहा है, इसलिए उनके पास कुछ प्रथम श्रेणी के कार्यों का समर्थन करने का कोई अवसर नहीं है, जैसे कि बाएं ऑपरेंड संदर्भित वस्तु के प्रकार के लिए स्वतंत्र रूप से सदस्य अभिगम संचालक । अन्य भाषाओं में उनके संदर्भों पर समान प्रतिबंध हो भी सकते हैं और नहीं भी।

C ++ में सन्दर्भ संभवत: विभिन्न भाषाओं में अर्थ को संरक्षित नहीं करेगा। उदाहरण के लिए, सामान्य रूप से संदर्भ C ++ जैसे मानों पर अमानवीय गुण नहीं लगाते हैं, इसलिए ऐसी धारणाएं कुछ अन्य भाषाओं में काम नहीं कर सकती हैं (और आपको काउंटरएक्सैम्पल काफी आसानी से मिल जाएंगे, जैसे जावा, सी #, ...)।

अभी भी सामान्य रूप से विभिन्न प्रोग्रामिंग भाषाओं में संदर्भों के बीच कुछ सामान्य गुण हो सकते हैं, लेकिन चलो इसे SO में कुछ अन्य प्रश्नों के लिए छोड़ दें।

(एक साइड नोट: प्रश्न किसी भी "सी- लाइक " भाषाओं की तुलना में पहले महत्वपूर्ण हो सकता है, जैसे कि ALGOL 68 / PL / I। )


16

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

#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;
}

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

#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;
}

15

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

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

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


5
आप यह लिख सकते हैं कि संदर्भ के रूप में &r + offsetकहां rघोषित किया गया था
MM

14

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

सादर, & rjj


1
संदर्भ और संकेत दोनों हैंडल हैं। वे दोनों आपको शब्दार्थ देते हैं जहां आपकी वस्तु संदर्भ द्वारा पारित हो जाती है, लेकिन संभाल की प्रतिलिपि बनाई जाती है। कोई फर्क नहीं। (हैंडल करने के अन्य तरीके भी हैं, जैसे कि डिक्शनरी में देखने के लिए एक कुंजी)
बेन वोइग्ट

मैं भी ऐसा ही सोचता था। लेकिन इससे जुड़े लेख को देखें कि ऐसा क्यों नहीं है।
आंद्रेजेज

2
@Andrzj: यह मेरी टिप्पणी में एकल वाक्य का सिर्फ एक लंबा संस्करण है: हैंडल की प्रतिलिपि बनाई गई है।
बेन वोइग्ट

मुझे इस पर अधिक स्पष्टीकरण की आवश्यकता है "हैंडल की प्रतिलिपि बनाई गई है"। मैं कुछ मूल विचार को समझता हूं लेकिन मुझे लगता है कि भौतिक रूप से संदर्भ और सूचक दोनों ही चर की स्मृति स्थान को इंगित करते हैं। क्या यह ऐसा है जैसे उपनाम अन्य मूल्य चर को संग्रहीत करता है और चर के मूल्य के रूप में इसे अपडेट करता है या कुछ और है? मैं नौसिखिया हूँ, और कृपया इसे एक मूर्खतापूर्ण प्रश्न के रूप में नहीं चिह्नित करें।
असीम

1
@Andrzej झूठा। दोनों मामलों में, पास-दर-मूल्य घटित हो रहा है। संदर्भ मान द्वारा पारित किया गया है और सूचक मूल्य से पारित किया गया है। कहना अन्यथा नए-नए भ्रम पैदा करता है।
मील्स राउत

13

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

#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 पर कॉल करें? ठीक है, स्पष्ट रूप से यह ठीक संकलन करता है, लेकिन रनटाइम में एक विभाजन दोष का कारण बनता है क्योंकि यह अब एक वैध चर पर इंगित नहीं कर रहा है, हमारे पास अनिवार्य रूप से एक टूटा हुआ संदर्भ है जो अभी भी मौजूद है (जब तक कि यह दायरे से बाहर हो जाता है), लेकिन बेकार है।

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

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

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

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


13

एक और अंतर यह है कि आप एक शून्य प्रकार के लिए संकेत दे सकते हैं (और इसका अर्थ है कि सूचक कुछ भी) लेकिन शून्य के संदर्भ निषिद्ध हैं।

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

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


12

इसके अलावा, एक संदर्भ जो एक फ़ंक्शन के लिए एक पैरामीटर है जो कि इनलाइन है, एक पॉइंटर की तुलना में अलग तरीके से संभाला जा सकता है।

void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
    int testptr=0;
    increment(&testptr);
}
void increftest()
{
    int testref=0;
    increment(testref);
}

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

बेशक, ऐसे फ़ंक्शंस के लिए जो पॉइंटर और रेफ़रेंस को इनलाइन नहीं करते हैं, एक ही कोड उत्पन्न करते हैं और यह हमेशा बेहतर होता है कि इंट्रेंसिक्स को संदर्भ द्वारा मान से बेहतर करें यदि वे संशोधित नहीं होते हैं और फ़ंक्शन द्वारा वापस आ जाते हैं।


10

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

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

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


10

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

#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


10

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

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

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

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

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

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

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

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


" निहित कास्ट " एक कलाकार एक वाक्य रचना है, यह व्याकरण में मौजूद है; एक कास्ट हमेशा स्पष्ट होता है
जिज्ञासु

9

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

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 का C ++ स्टाइल दिशानिर्देश इसका एक उदाहरण है।


8

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

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

6

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

एक पॉइंटर को 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 "="

1
हम अभी भी हो सकते हैं const int& i = 0
Revolver_Ocelot

1
इस मामले में संदर्भ केवल पढ़ने में उपयोग किया जाएगा हम "const_cast" का उपयोग करते हुए भी इस कॉन्स्टेंस संदर्भ को संशोधित नहीं कर सकते हैं क्योंकि "const_cast" केवल पॉइंटर को स्वीकार नहीं करते हैं।
ढोकर

1
const_cast संदर्भों के साथ बहुत अच्छी तरह से काम करता है: coliru.stacked-crooked.com/a/eebb454ab2cfd570
Revolver_Ocelot

1
आप एक संदर्भ बनाने की कोशिश नहीं कर रहे हैं एक संदर्भ कास्टिंग इस कोशिश; const int & i =; const_cast <int> (i); मैं संदर्भ के निरंतरता को फेंकने की कोशिश करता हूं ताकि संदर्भ के लिए संभव हो और नए मूल्य का असाइनमेंट हो सके लेकिन यह संभव नहीं है। कृपया ध्यान दें !!
dhokar.w

5

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


4

सरल शब्दों में, हम कह सकते हैं कि एक संदर्भ एक चर के लिए एक वैकल्पिक नाम है जबकि, एक सूचक एक चर है जो दूसरे चर का पता रखता है। जैसे

int a = 20;
int &r = a;
r = 40;  /* now the value of a is changed to 40 */

int b =20;
int *ptr;
ptr = &b;  /*assigns address of b to ptr not the value */
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.