मुझे पता है कि संदर्भ वाक्यात्मक चीनी हैं, इसलिए कोड को पढ़ना और लिखना आसान है।
लेकिन अंतर क्या हैं?
int &x = *(int*)0;
जीसीसी पर। संदर्भ वास्तव में NULL को इंगित कर सकता है।
मुझे पता है कि संदर्भ वाक्यात्मक चीनी हैं, इसलिए कोड को पढ़ना और लिखना आसान है।
लेकिन अंतर क्या हैं?
int &x = *(int*)0;
जीसीसी पर। संदर्भ वास्तव में NULL को इंगित कर सकता है।
जवाबों:
एक पॉइंटर को फिर से सौंपा जा सकता है:
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;
एक पॉइंटर का अपना मेमोरी एड्रेस और स्टैक पर आकार (x86 पर 4 बाइट्स) होता है, जबकि एक संदर्भ एक ही मेमोरी एड्रेस (मूल चर के साथ) साझा करता है, लेकिन स्टैक पर कुछ जगह भी लेता है। चूँकि किसी संदर्भ का मूल चर के समान ही पता होता है, इसलिए संदर्भ को उसी चर के लिए किसी अन्य नाम के रूप में सोचना सुरक्षित होता है। नोट: एक पॉइंटर पॉइंट जो स्टैक या हीप पर हो सकता है। Ditto एक संदर्भ। इस कथन में मेरा दावा यह नहीं है कि एक सूचक को स्टैक की ओर इशारा करना चाहिए। एक पॉइंटर सिर्फ एक वैरिएबल है जो मेमोरी एड्रेस रखता है। यह चर स्टैक पर है। चूँकि स्टैक पर एक रेफरेंस का अपना स्पेस होता है, और चूंकि एड्रेस वैसा ही होता है जैसा कि वह वेरिएबल होता है। के बारे में अधिक ढेर ढेर बनाम। इसका मतलब यह है कि एक संदर्भ का वास्तविक पता है जो संकलक आपको नहीं बताएगा।
int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
आपके पास अप्रत्यक्ष स्तर के अतिरिक्त स्तर की पेशकश करने वाले पॉइंटर्स टू पॉइंटर्स टू पॉइंटर्स हो सकते हैं। जबकि संदर्भ केवल परोक्ष के एक स्तर की पेशकश करते हैं।
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);
एक सूचक को 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
संकेत एक सरणी पर पुनरावृति कर सकते हैं; आप ++
अगले आइटम पर जाने के लिए उपयोग कर सकते हैं जो एक संकेतक इंगित कर रहा है, और + 4
5 वें तत्व पर जाने के लिए। यह कोई बात नहीं है कि ऑब्जेक्ट किस आकार का है जो सूचक को इंगित करता है।
एक पॉइंटर को *
उसके द्वारा इंगित की गई मेमोरी लोकेशन तक पहुंचने के लिए डिरेल करने की आवश्यकता होती है , जबकि एक संदर्भ को सीधे इस्तेमाल किया जा सकता है। एक वर्ग / संरचना के लिए एक सूचक ->
इसका उपयोग करने के लिए उपयोग करता है जबकि एक संदर्भ एक का उपयोग करता है .
।
संदर्भ को एक सरणी में नहीं भरा जा सकता है, जबकि संकेत हो सकते हैं (उपयोगकर्ता @ नोट द्वारा उल्लेख किया गया है)
कांस्टिट्यूशन अस्थायी लोगों के लिए बाध्य हो सकते हैं। संकेत (कुछ अप्रत्यक्ष के बिना नहीं) नहीं कर सकते हैं:
const int &x = int(12); //legal C++
int *y = &int(12); //illegal to dereference a temporary.
यह const&
तर्क सूचियों में उपयोग के लिए सुरक्षित बनाता है और इसके बाद।
एक संदर्भ को एक निरंतर सूचक के रूप में सोचा जा सकता है (स्वचालित मूल्य पर सूचक के साथ एक निरंतर मूल्य के लिए भ्रमित नहीं होना चाहिए!) संकलक *
आपके लिए ऑपरेटर लागू करेगा ।
सभी संदर्भों को एक गैर-शून्य मान के साथ आरंभीकृत किया जाना चाहिए या संकलन विफल हो जाएगा। संदर्भ का पता प्राप्त करना न तो संभव है - पता ऑपरेटर बदले में संदर्भित मान का पता लौटाएगा - और न ही संदर्भों पर अंकगणित करना संभव है।
C प्रोग्रामर C ++ संदर्भों को नापसंद कर सकते हैं क्योंकि यह अब स्पष्ट नहीं होगा जब अप्रत्यक्ष रूप से होता है या यदि कोई फ़ंक्शन फ़ंक्शन हस्ताक्षरों को देखे बिना मान या सूचक द्वारा पास हो जाता है।
C ++ प्रोग्रामर पॉइंटर्स का उपयोग करना नापसंद कर सकते हैं क्योंकि वे असुरक्षित माने जाते हैं - हालाँकि संदर्भ वास्तव में निरंतर पॉइंटर्स की तुलना में किसी भी सबसे सुरक्षित मामलों को छोड़कर अधिक सुरक्षित नहीं हैं - स्वचालित अप्रत्यक्षता की सुविधा का अभाव है और एक अलग शब्दार्थ अर्थ ले जाता है।
C ++ से निम्नलिखित कथन पर विचार करें :
भले ही एक संदर्भ अक्सर अंतर्निहित विधानसभा भाषा में एक पते का उपयोग करके लागू किया जाता है, कृपया किसी ऑब्जेक्ट के लिए एक मज़ेदार दिखने वाले सूचक के रूप में एक संदर्भ के बारे में न सोचें। एक संदर्भ है वस्तु। यह न तो ऑब्जेक्ट का पॉइंटर है, न ही ऑब्जेक्ट की कॉपी। यह है वस्तु।
लेकिन अगर एक संदर्भ वास्तव में वस्तु थे, तो झूलने वाले संदर्भ कैसे हो सकते हैं? अप्रबंधित भाषाओं में, किसी भी बिंदु के संदर्भ में 'सुरक्षित' होना असंभव है - आम तौर पर गुंजाइश सीमाओं के पार मज़बूती से उर्फ मूल्यों के लिए एक रास्ता नहीं है!
C बैकग्राउंड से आते हुए, C ++ संदर्भ कुछ हद तक मूर्खतापूर्ण अवधारणा की तरह लग सकता है, लेकिन किसी को अभी भी संकेत के बजाय उनका उपयोग करना चाहिए जहां संभव हो: स्वचालित अप्रत्यक्ष रूप से सुविधाजनक है, और RAII से निपटने के दौरान संदर्भ विशेष रूप से उपयोगी हो जाते हैं - लेकिन किसी भी सुरक्षा के कारण नहीं लाभ, बल्कि इसलिए क्योंकि वे मुहावरेदार कोड को कम अजीब बनाते हैं।
RAII C ++ की केंद्रीय अवधारणाओं में से एक है, लेकिन यह गैर-तुच्छ रूप से नकल करने वाले शब्दार्थों के साथ परस्पर क्रिया करती है। संदर्भ द्वारा वस्तुओं को पास करना इन मुद्दों से बचा जाता है क्योंकि कोई भी नकल शामिल नहीं है। यदि संदर्भ भाषा में मौजूद नहीं थे, तो आपको इसके बजाय पॉइंटर्स का उपयोग करना होगा, जो कि उपयोग करने के लिए अधिक बोझिल हैं, इस प्रकार भाषा डिजाइन सिद्धांत का उल्लंघन करते हैं कि सबसे अच्छा-अभ्यास समाधान विकल्पों की तुलना में आसान होना चाहिए।
यदि आप वास्तव में पांडित्यपूर्ण होना चाहते हैं, तो एक बात है जिसे आप एक संदर्भ के साथ कर सकते हैं जो आप एक सूचक के साथ नहीं कर सकते हैं: एक अस्थायी वस्तु के जीवनकाल का विस्तार करें। 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
इसे संकलित करने में विफल होना चाहिए। आप एक नॉन-कॉस्ट रेफरेंस को एक अस्थायी ऑब्जेक्ट से नहीं बांध सकते, न ही आप इस मामले के लिए उसका पता ले सकते हैं।
const &
बाइंडिंग के तथ्य द्वारा विस्तारित किया जाता है , और केवल जब संदर्भ दायरे से बाहर चला जाता है तो वास्तविक संदर्भित प्रकार के विध्वंसक (संदर्भ प्रकार की तुलना में, जो आधार हो सकता है) कहा जाता है। चूंकि यह एक संदर्भ है, इसके बीच में कोई स्लाइसिंग नहीं होगी।
Animal x = fast ? getHare() : getTortoise()
तो x
क्लासिक स्लाइसिंग समस्या का सामना करना पड़ेगा, जबकि Animal& x = ...
सही तरीके से काम करेगा।
सिंटैक्टिक शुगर के अलावा, एक संदर्भ एक const
सूचक ( पॉइंटर नहीं है const
)। जब आप संदर्भ चर घोषित करते हैं, तो आपको इसे स्थापित करना होगा और बाद में आप इसे बदल नहीं सकते।
अद्यतन: अब जब मैं इसके बारे में कुछ और सोचता हूं, तो एक महत्वपूर्ण अंतर है।
एक कास्ट पॉइंटर के लक्ष्य को इसका पता लेने और एक कास्ट कास्ट का उपयोग करके बदला जा सकता है।
संदर्भ का लक्ष्य किसी भी तरह से यूबी से कम नहीं बदला जा सकता है।
यह संकलक को एक संदर्भ पर अधिक अनुकूलन करने की अनुमति देनी चाहिए।
T* const
अलग-अलग वाक्य रचना चीनी के साथ संदर्भों को समझा है (जो आपके कोड से बहुत * और & को खत्म करने के लिए होता है)।
int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci);
ठीक है।
लोकप्रिय राय के विपरीत, ऐसा संदर्भ होना संभव है जो 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));
बेहतर लेखन कौशल वाले किसी व्यक्ति की इस समस्या के बारे में अधिक जानकारी के लिए, जिम हिसलोप और हर्ब सटर से नल संदर्भ देखें ।
डेरेफ्रेंसिंग के खतरों के एक अन्य उदाहरण के लिए एक शून्य सूचक रेमंड चेन द्वारा दूसरे प्लेटफॉर्म पर कोड को पोर्ट करने की कोशिश करते समय अपरिभाषित व्यवहार को देखें ।
आप सबसे महत्वपूर्ण हिस्सा भूल गए:
सदस्य-अभिगम बिंदुओं के ->
साथ संदर्भ-उपयोग के साथ सदस्य-अभिगम का उपयोग करता है.
foo.bar
है स्पष्ट रूप से बेहतर करने के लिए foo->bar
एक ही रास्ता है कि में vi है स्पष्ट रूप से बेहतर करने के लिए Emacs :-)
->
पॉइंटर्स के संदर्भ में प्रदान करता है, जैसे कि पॉइंटर के साथ ही।
.
और ->
vi और emacs के साथ कुछ करना है :)
.
करना बेहतर है ->
, लेकिन vi बनाम emacs की तरह, यह पूरी तरह से व्यक्तिपरक है और आप कुछ भी साबित नहीं कर सकते हैं
संदर्भ बहुत हद तक संकेत के समान हैं, लेकिन वे विशेष रूप से संकलित करने में सहायक होने के लिए तैयार किए जाते हैं।
उदहारण के लिए:
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
साबित करने की तुलना में काफी आसान है कि सूचक अंकगणित का एक गुच्छा उत्पन्न नहीं होता है।
void maybeModify(int& x) { 1[&x]++; }
, जिसके ऊपर अन्य टिप्पणियां चर्चा कर रही हैं
दरअसल, एक संदर्भ वास्तव में एक सूचक की तरह नहीं है।
एक संकलक चर को "संदर्भ" रखता है, एक नाम को स्मृति पते के साथ जोड़ रहा है; संकलन करते समय किसी भी चर नाम को मेमोरी एड्रेस में अनुवाद करना उसका काम है।
जब आप एक संदर्भ बनाते हैं, तो आप केवल संकलक को बताते हैं कि आप पॉइंटर चर का दूसरा नाम असाइन करते हैं; इसीलिए संदर्भ "इंगित करने के लिए अशक्त" नहीं हो सकता है, क्योंकि एक चर नहीं हो सकता है, और नहीं हो सकता है।
संकेत चर हैं; उनमें कुछ अन्य चर का पता होता है, या अशक्त हो सकता है। महत्वपूर्ण बात यह है कि एक पॉइंटर का एक मूल्य है, जबकि एक संदर्भ में केवल एक चर है जिसे वह संदर्भित कर रहा है।
अब असली कोड की कुछ व्याख्या:
int a = 0;
int& b = a;
यहां आप एक और चर नहीं बना रहे हैं जो इंगित करता है a
; आप केवल स्मृति सामग्री का एक और नाम जोड़ रहे हैं, जिसका मान रखा गया है a
। इस मेमोरी के अब दो नाम हैं, a
और b
, इसे किसी भी नाम का उपयोग करके संबोधित किया जा सकता है।
void increment(int& n)
{
n = n + 1;
}
int a;
increment(a);
किसी फ़ंक्शन को कॉल करते समय, कंपाइलर आमतौर पर कॉपी किए जाने वाले तर्कों के लिए मेमोरी स्पेस बनाता है। फ़ंक्शन हस्ताक्षर उन रिक्त स्थानों को परिभाषित करता है जिन्हें बनाया जाना चाहिए और इन रिक्त स्थान के लिए उपयोग किया जाने वाला नाम देता है। एक पैरामीटर को एक संदर्भ के रूप में घोषित करना कंपाइलर को विधि कॉल के दौरान एक नया मेमोरी स्पेस आवंटित करने के बजाय इनपुट चर मेमोरी स्पेस का उपयोग करने के लिए कहता है। यह कहना अजीब लग सकता है कि आपका फ़ंक्शन सीधे कॉलिंग स्कोप में घोषित एक वैरिएबल में हेरफेर करेगा, लेकिन याद रखें कि संकलित कोड निष्पादित करते समय, अधिक गुंजाइश नहीं है; बस सादा सपाट मेमोरी है, और आपका फ़ंक्शन कोड किसी भी चर को जोड़ सकता है।
अब ऐसे कुछ मामले हो सकते हैं जहां आपके कंपाइलर को कंपाइल करते समय संदर्भ का पता नहीं चल पाता है, जैसे कि एक्सटर वेरिएबल का उपयोग करते समय। तो एक संदर्भ अंतर्निहित कोड में एक सूचक के रूप में लागू हो सकता है या नहीं भी हो सकता है। लेकिन मैंने आपको जो उदाहरण दिए हैं, उनमें यह संभवत: एक सूचक के साथ लागू नहीं होगा।
एक संदर्भ कभी नहीं हो सकता NULL
।
void Foo::bar() { virtual_baz(); }
उस segfaults जैसा कोड होता है। यदि आप इस बात से अवगत नहीं हैं कि संदर्भ शून्य हो सकते हैं, तो आप अशक्त को उसके मूल में वापस नहीं ले जा सकते।
int &r=*p;
अपरिभाषित व्यवहार है। उस बिंदु पर, यदि आप एक नहीं है "शून्य के संदर्भ की ओर इशारा करते," यदि आप एक प्रोग्राम है जो है नहीं रह गया है के बारे में समझाया जा सकता है सब पर ।
जबकि संदर्भ और संकेत दोनों का उपयोग अप्रत्यक्ष रूप से दूसरे मूल्य तक पहुंचने के लिए किया जाता है, संदर्भ और संकेत के बीच दो महत्वपूर्ण अंतर हैं। पहला यह है कि एक संदर्भ हमेशा एक वस्तु को संदर्भित करता है: यह एक संदर्भ को परिभाषित किए बिना इसे परिभाषित करने के लिए एक त्रुटि है। असाइनमेंट का व्यवहार दूसरा महत्वपूर्ण अंतर है: एक संदर्भ को असाइन करने से उस वस्तु को बदल दिया जाता है जिससे संदर्भ बाध्य है; यह किसी अन्य ऑब्जेक्ट के संदर्भ में विद्रोह नहीं करता है। एक बार आरंभ करने के बाद, एक संदर्भ हमेशा एक ही अंतर्निहित वस्तु को संदर्भित करता है।
इन दो कार्यक्रम के टुकड़े पर विचार करें। पहले में, हम एक पॉइंटर को दूसरे को असाइन करते हैं:
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 को बदल देता है, आरआई द्वारा संदर्भित मूल्य, और संदर्भ ही नहीं। असाइनमेंट के बाद, दो संदर्भ अभी भी अपनी मूल वस्तुओं को संदर्भित करते हैं, और उन वस्तुओं का मूल्य अब भी समान है।
एक शब्दार्थिक अंतर है जो गूढ़ प्रकट हो सकता है यदि आप एक सार या यहां तक कि अकादमिक फैशन में कंप्यूटर भाषाओं का अध्ययन करने से परिचित नहीं हैं।
उच्चतम स्तर पर, संदर्भों का विचार यह है कि वे पारदर्शी "उपनाम" हैं। आपका कंप्यूटर उन्हें काम करने के लिए एक पते का उपयोग कर सकता है, लेकिन आपको इसके बारे में चिंता करने की ज़रूरत नहीं है: आप उन्हें मौजूदा वस्तु के लिए "बस एक और नाम" के रूप में सोचने वाले हैं और वाक्यविन्यास यह दर्शाता है। वे पॉइंटर्स की तुलना में सख्त हैं, इसलिए जब आप एक झूलने वाले पॉइंटर बनाने वाले होते हैं, तो आपके कंपाइलर आपको मज़बूती से संदर्भ देने की चेतावनी दे सकते हैं।
इससे परे, निश्चित रूप से संकेत और संदर्भ के बीच कुछ व्यावहारिक अंतर हैं। उनका उपयोग करने के लिए वाक्यविन्यास स्पष्ट रूप से अलग है, और आप संदर्भों को "पुन: सीट" नहीं कर सकते हैं, कुछ भी संदर्भ नहीं है, या संदर्भ के लिए संकेत हैं।
एक संदर्भ दूसरे चर के लिए एक उपनाम है जबकि एक सूचक एक चर के स्मृति पते को रखता है। संदर्भों को आम तौर पर फ़ंक्शन मापदंडों के रूप में उपयोग किया जाता है ताकि पारित वस्तु प्रतिलिपि न हो बल्कि वस्तु ही हो।
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.
इससे कोई फर्क नहीं पड़ता कि यह कितना स्थान लेता है क्योंकि आप वास्तव में जो भी जगह लेगा उसका कोई साइड इफेक्ट (कोड निष्पादित किए बिना) नहीं देख सकता है।
दूसरी ओर, संदर्भ और संकेत के बीच एक बड़ा अंतर यह है कि अस्थायी संदर्भों को सौंपा गया संदर्भ तब तक जीवित रहता है जब तक कि संदर्भ संदर्भ से बाहर नहीं हो जाता है।
उदाहरण के लिए:
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!
यह भाषा तंत्र है जो स्कोपगार्ड को काम करने की अनुमति देता है।
यह ट्यूटोरियल पर आधारित है । जो लिखा गया है वह इसे और स्पष्ट करता है:
>>> 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
।
nullptr
? क्या आपने वास्तव में इस धागे का कोई अन्य हिस्सा पढ़ा है, या ...?
एक संदर्भ कुछ स्मृति को दिया गया दूसरा नाम नहीं है। यह एक अपरिवर्तनीय सूचक है जो स्वचालित रूप से उपयोग पर डी-संदर्भित है। मूल रूप से यह उबलता है:
int& j = i;
यह आंतरिक रूप से बन जाता है
int* const j = &i;
const
पॉइंटर को लागू करने का एक वैध तरीका है । यह लचीलापन साबित नहीं करता है कि एक संदर्भ और एक सूचक के बीच अंतर है।
C ++ में एक संदर्भ क्या है? प्रकार के कुछ विशिष्ट उदाहरण जो ऑब्जेक्ट प्रकार नहीं है ।
C ++ में पॉइंटर क्या है? प्रकार के कुछ विशिष्ट उदाहरण जो एक वस्तु प्रकार है ।
से ऑब्जेक्ट प्रकार की आईएसओ सी ++ परिभाषा :
एक वस्तु प्रकार (संभवत: है सीवी -qualified) प्रकार है कि एक समारोह का प्रकार, नहीं एक संदर्भ प्रकार, और नहीं नहीं है सीवी शून्य।
यह जानना महत्वपूर्ण हो सकता है, ऑब्जेक्ट प्रकार C ++ में ब्रह्मांड के शीर्ष-स्तरीय श्रेणी है। संदर्भ भी एक शीर्ष-स्तरीय श्रेणी है। लेकिन पॉइंटर नहीं है।
यौगिक प्रकार के संदर्भ में बिंदुओं और संदर्भों का एक साथ उल्लेख किया गया है । यह मूल रूप से घोषित (और विस्तारित) सी से विरासत में मिलाया गया सिंटैक्स की प्रकृति के कारण है, जिसका कोई संदर्भ नहीं है। (इसके अलावा, C ++ 11 के बाद से एक से अधिक प्रकार के संदर्भों के घोषणाकर्ता हैं, जबकि पॉइंटर्स अभी भी "unityped:: &
+ &&
बनाम *
।) हैं, इसलिए इस संदर्भ में C की समान शैली के साथ" एक्सटेंशन "द्वारा विशिष्ट भाषा का मसौदा तैयार करना कुछ हद तक उचित है। । (मैं अभी भी तर्क दूंगा कि घोषणाकर्ताओं का वाक्यविन्यास वाक्य-विन्यास की व्यर्थता को बहुत व्यर्थ कर देता है, मानव उपयोगकर्ताओं और कार्यान्वयन दोनों को निराश करता है। इस प्रकार, वे सभी अंतर्निहित होने के योग्य नहीं हैं ।एक नई भाषा डिजाइन में। यह पीएल डिजाइन के बारे में एक पूरी तरह से अलग विषय है, हालांकि।)
अन्यथा, यह महत्वहीन है कि संकेत एक साथ संदर्भ के साथ विशिष्ट प्रकार के प्रकार के रूप में योग्य हो सकते हैं। वे सिंटैक्स समानता के अलावा बस कुछ सामान्य गुणों को साझा करते हैं, इसलिए उन्हें ज्यादातर मामलों में एक साथ रखने की कोई आवश्यकता नहीं है।
ऊपर दिए गए कथनों को केवल "संकेत" और प्रकार के रूप में "संदर्भ" का उल्लेख करें। उनके उदाहरणों (जैसे चर) के बारे में कुछ दिलचस्पी सवाल हैं। वहाँ भी कई भ्रांतियाँ आती हैं।
शीर्ष-स्तरीय श्रेणियों के अंतर पहले से ही कई ठोस अंतरों को प्रकट कर सकते हैं जो सीधे संकेत करने के लिए बंधे नहीं हैं:
cv
क्वालिफायर हो सकते हैं । सन्दर्भ नहीं दे सकते।संदर्भों पर कुछ और विशेष नियम:
&&
टेम्पलेट पैरामीटर कटौती के दौरान संदर्भ ढहने के आधार पर मापदंडों पर विशेष नियम ("अग्रेषण संदर्भ" के रूप में) मापदंडों के "पूर्ण अग्रेषण" की अनुमति देते हैं ।std::initializer_list
संदर्भ संदर्भ आजीवन विस्तार के कुछ समान नियमों का पालन करते हैं। यह कीड़े का दूसरा तरीका है।मुझे पता है कि संदर्भ वाक्यात्मक चीनी हैं, इसलिए कोड को पढ़ना और लिखना आसान है।
तकनीकी रूप से, यह सादा गलत है। सन्दर्भ C ++ में किसी भी अन्य विशेषताओं का वाक्यगत शर्करा नहीं है, क्योंकि वे किसी भी अर्थ भिन्नता के बिना अन्य विशेषताओं द्वारा बिल्कुल प्रतिस्थापित नहीं किए जा सकते हैं।
(इसी तरह, लंबोदर-एक्सप्रेशन एस C ++ में किसी भी अन्य विशेषताओं की सिंथैटिक शुगर नहीं है क्योंकि इसे कैप्चर किए गए वेरिएबल्स के डिक्लेरेशन ऑर्डर की तरह "अनिर्दिष्ट" गुणों के साथ सटीक रूप से सिम्युलेटेड नहीं किया जा सकता है , जो महत्वपूर्ण हो सकता है क्योंकि ऐसे वेरिएबल्स का इनिशियलाइज़ेशन ऑर्डर हो सकता है महत्वपूर्ण।)
इस सख्त अर्थ में C ++ में केवल कुछ प्रकार के सिंथैटिक शर्करा होते हैं। एक उदाहरण है (C से विरासत में मिला) बिल्ट-इन (नॉन-ओवरलोडेड) ऑपरेटर []
, जिसे बिल्ट-इन ऑपरेटर यूनरी *
और बाइनरी पर संयोजन के विशिष्ट रूपों के समान अर्थ गुण होते हैं+
।
तो, एक पॉइंटर और एक संदर्भ दोनों मेमोरी की समान मात्रा का उपयोग करते हैं।
ऊपर बयान केवल गलत है। इस तरह की भ्रांतियों से बचने के लिए ISO C ++ नियम देखें।
से [intro.object] / 1 :
... एक वस्तु अपने पूरे जीवनकाल में, और विनाश की अपनी अवधि में, निर्माण की अवधि में भंडारण के एक क्षेत्र पर कब्जा कर लेती है। ...
से [dcl.ref] / 4 :
यह अनिर्दिष्ट है कि किसी संदर्भ में भंडारण की आवश्यकता है या नहीं।
ध्यान दें कि ये शब्दार्थ गुण हैं।
यहां तक कि बिंदुओं को भाषा डिज़ाइन के संदर्भ में संदर्भों के साथ एक साथ रखने के लिए पर्याप्त योग्य नहीं है, फिर भी कुछ तर्क हैं जो कुछ अन्य संदर्भों में उनके बीच चयन करने के लिए बहस करने योग्य बनाते हैं, उदाहरण के लिए, जब पैरामीटर प्रकारों पर विकल्प बनाते हैं।
लेकिन कहानी यहीं पर खत्म नहीं हो जाती। मेरा मतलब है कि बिंदुओं बनाम संदर्भों की तुलना में अधिक चीजें हैं जिन्हें आपको विचार करना होगा।
यदि आपको ऐसे अति-विशिष्ट विकल्पों पर नहीं टिकना है, तो ज्यादातर मामलों में जवाब छोटा है: आपको पॉइंटर्स का उपयोग करने की आवश्यकता नहीं है, इसलिए आप नहीं करते हैं । पॉइंटर्स आमतौर पर काफी खराब होते हैं क्योंकि वे बहुत सी ऐसी चीज़ों की अपेक्षा करते हैं जिनकी आप अपेक्षा नहीं करते हैं और वे कोड बनाए रखने की क्षमता और (यहां तक कि) पोर्टेबिलिटी को कम करते हुए कई अंतर्निहित धारणाओं पर भरोसा करेंगे। संकेत पर अनावश्यक रूप से भरोसा करना निश्चित रूप से एक बुरी शैली है और इसे आधुनिक C ++ के अर्थ में टाला जाना चाहिए। अपने उद्देश्य पर पुनर्विचार करें और आप अंततः पाएंगे कि सूचक अधिकांश मामलों में अंतिम प्रकार की विशेषता है ।
&
संदर्भ प्रकार की आवश्यकता होती है जैसे कि 1 पैरामीटर प्रकार। (और आमतौर पर यह const
योग्य होना चाहिए ।)&&
रेफरेंस टाइप की आवश्यकता होती है, जैसे 1 पैरामीटर टाइप। (और आमतौर पर कोई क्वालिफायर नहीं होना चाहिए।)operator=
विशेष सदस्य कार्यों के रूप में अतिभारित करने के लिए प्रतिलिपि / चाल बिल्डरों के 1 पैरामीटर के समान संदर्भ प्रकारों की आवश्यकता होती है।++
को डमी की आवश्यकता होती है int
।unique_ptr
और shared_ptr
(या यहां तक कि अपने आप को द्वारा homebrew लोगों के साथ अगर आप उन्हें आवश्यकता होने के लिए अपारदर्शी , बल्कि कच्चे संकेत से)।std::optional
, तो कच्चे पॉइंटर्स के बजाय रैपर का उपयोग करें ।observer_ptr
लाइब्रेरी फंडामेंटल टीएस।वर्तमान भाषा में केवल अपवादों पर काम नहीं किया जा सकता है:
operator new
। (हालांकि, cv - void*
अभी भी सामान्य ऑब्जेक्ट पॉइंटर्स की तुलना में काफी अलग और सुरक्षित है क्योंकि यह अप्रत्याशित पॉइंटर अंकगणित को नियंत्रित करता है जब तक कि आप void*
GNU जैसे कुछ गैर-अनुरूपता विस्तार पर भरोसा नहीं कर रहे हैं ।)तो, व्यवहार में, जवाब इतना स्पष्ट है: जब संदेह में, संकेत से बचें । आपको पॉइंटर्स का उपयोग केवल तभी करना होगा जब बहुत स्पष्ट कारण हों कि कुछ और उपयुक्त नहीं है। ऊपर उल्लेख किए गए कुछ असाधारण मामलों को छोड़कर, ऐसे विकल्प लगभग हमेशा शुद्ध रूप से सी ++ नहीं होते हैं - विशिष्ट (लेकिन भाषा-कार्यान्वयन-विशिष्ट होने की संभावना है)। ऐसे उदाहरण हो सकते हैं:
यदि आप कुछ Google खोज परिणाम (C ++ के लिए विशिष्ट नहीं) के माध्यम से प्रश्न देखते हैं , तो यह गलत जगह होने की संभावना है।
C ++ में सन्दर्भ काफी "विषम" है, क्योंकि यह अनिवार्य रूप से प्रथम श्रेणी का नहीं है: उन्हें वस्तुओं के रूप में माना जाएगा या जिन कार्यों को संदर्भित किया जा रहा है, इसलिए उनके पास कुछ प्रथम श्रेणी के कार्यों का समर्थन करने का कोई अवसर नहीं है, जैसे कि बाएं ऑपरेंड । संदर्भित वस्तु के प्रकार के लिए स्वतंत्र रूप से सदस्य अभिगम संचालक । अन्य भाषाओं में उनके संदर्भों पर समान प्रतिबंध हो भी सकते हैं और नहीं भी।
C ++ में सन्दर्भ संभवत: विभिन्न भाषाओं में अर्थ को संरक्षित नहीं करेगा। उदाहरण के लिए, सामान्य रूप से संदर्भ C ++ जैसे मानों पर अमानवीय गुण नहीं लगाते हैं, इसलिए ऐसी धारणाएं कुछ अन्य भाषाओं में काम नहीं कर सकती हैं (और आपको काउंटरएक्सैम्पल काफी आसानी से मिल जाएंगे, जैसे जावा, सी #, ...)।
अभी भी सामान्य रूप से विभिन्न प्रोग्रामिंग भाषाओं में संदर्भों के बीच कुछ सामान्य गुण हो सकते हैं, लेकिन चलो इसे SO में कुछ अन्य प्रश्नों के लिए छोड़ दें।
(एक साइड नोट: प्रश्न किसी भी "सी- लाइक " भाषाओं की तुलना में पहले महत्वपूर्ण हो सकता है, जैसे कि ALGOL 68 / PL / I। )
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;
}
जब तक मुझे इनमें से किसी एक की आवश्यकता होती है, मैं संदर्भों का उपयोग करता हूं:
नल बिंदुओं को एक प्रहरी मूल्य के रूप में इस्तेमाल किया जा सकता है, अक्सर फ़ंक्शन के अतिभार या मल के उपयोग से बचने का एक सस्ता तरीका है।
आप एक सूचक पर अंकगणित कर सकते हैं। उदाहरण के लिए,p += offset;
&r + offset
कहां r
घोषित किया गया था
बिंदुओं और संदर्भों के बीच एक मौलिक अंतर है जो मैंने किसी को भी नहीं देखा था: संदर्भ फ़ंक्शन तर्क में पास-बाय-संदर्भ शब्दार्थ को सक्षम करते हैं। संकेत, हालांकि यह पहली बार में दिखाई नहीं देता है: वे केवल पास-दर-मूल्य शब्दार्थ प्रदान करते हैं। यह इस लेख में बहुत अच्छी तरह से वर्णित किया गया है ।
सादर, & rjj
भ्रम में जोड़ने के जोखिम पर, मैं कुछ इनपुट में फेंकना चाहता हूं, मुझे यकीन है कि यह ज्यादातर इस बात पर निर्भर करता है कि संकलक कैसे संदर्भों को लागू करता है, लेकिन इस विचार के मामले में कि एक संदर्भ केवल स्टैक पर एक चर को इंगित कर सकता है वास्तव में सही नहीं है, उदाहरण के लिए इसे लें:
#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 पर कॉल करें? ठीक है, स्पष्ट रूप से यह ठीक संकलन करता है, लेकिन रनटाइम में एक विभाजन दोष का कारण बनता है क्योंकि यह अब एक वैध चर पर इंगित नहीं कर रहा है, हमारे पास अनिवार्य रूप से एक टूटा हुआ संदर्भ है जो अभी भी मौजूद है (जब तक कि यह दायरे से बाहर हो जाता है), लेकिन बेकार है।
दूसरे शब्दों में, एक संदर्भ कुछ भी नहीं है, लेकिन एक पॉइंटर है जो पॉइंटर मैकेनिक्स को अलग कर दिया गया है, जिससे यह सुरक्षित और उपयोग करने में आसान है (कोई आकस्मिक सूचक गणित, कोई मिश्रण नहीं। '' और '->', आदि), आपको मानते हुए। ऊपर मेरे उदाहरणों की तरह किसी भी बकवास की कोशिश न करें;)
अब इस बात की परवाह किए बिना कि कोई कंपाइलर संदर्भों को कैसे संभालता है, इसमें हमेशा हुड के नीचे किसी न किसी तरह का पॉइंटर होता है, क्योंकि एक संदर्भ को विशिष्ट मेमोरी एड्रेस पर एक विशिष्ट वैरिएबल का उल्लेख करना चाहिए , क्योंकि यह अपेक्षित रूप से काम करने के लिए होता है, इसके आस-पास कोई भी नहीं होता है (इसलिए शब्द 'संदर्भ')।
संदर्भों के साथ याद रखने के लिए महत्वपूर्ण एकमात्र नियम यह है कि उन्हें घोषणा के समय परिभाषित किया जाना चाहिए (हेडर में एक संदर्भ के अपवाद के साथ, उस स्थिति में इसे कंस्ट्रक्टर में परिभाषित किया जाना चाहिए, ऑब्जेक्ट के निहित होने के बाद इसे परिभाषित करने में बहुत देर हो चुकी है)।
याद रखें, मेरे उदाहरण ऊपर दिए गए हैं, उदाहरण यह दर्शाते हैं कि एक संदर्भ क्या है, आप उन तरीकों से संदर्भ का उपयोग कभी नहीं करना चाहेंगे! एक संदर्भ के उचित उपयोग के लिए यहाँ पहले से ही बहुत सारे उत्तर हैं जो सिर पर कील मारते हैं
एक और अंतर यह है कि आप एक शून्य प्रकार के लिए संकेत दे सकते हैं (और इसका अर्थ है कि सूचक कुछ भी) लेकिन शून्य के संदर्भ निषिद्ध हैं।
int a;
void * p = &a; // ok
void & p = a; // forbidden
मैं नहीं कह सकता कि मैं वास्तव में इस विशेष अंतर से खुश हूं। मैं बहुत पसंद करूंगा कि इसे किसी भी पते के साथ अर्थ संदर्भ के साथ अनुमति दी जाएगी और अन्यथा संदर्भों के लिए समान व्यवहार। यह सी लाइब्रेरी फ़ंक्शंस के कुछ समतुल्यों को परिभाषित करने की अनुमति देगा जैसे संदर्भों का उपयोग करके मेम्ची।
इसके अलावा, एक संदर्भ जो एक फ़ंक्शन के लिए एक पैरामीटर है जो कि इनलाइन है, एक पॉइंटर की तुलना में अलग तरीके से संभाला जा सकता है।
void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
int testptr=0;
increment(&testptr);
}
void increftest()
{
int testref=0;
increment(testref);
}
कई कंपाइलर जब पॉइंटर वर्जन को इनलाइन करते हैं तो वास्तव में मेमोरी को राइट कर देगा (हम एड्रेस को स्पष्ट रूप से ले रहे हैं)। हालांकि, वे संदर्भ को एक रजिस्टर में छोड़ देंगे जो अधिक इष्टतम है।
बेशक, ऐसे फ़ंक्शंस के लिए जो पॉइंटर और रेफ़रेंस को इनलाइन नहीं करते हैं, एक ही कोड उत्पन्न करते हैं और यह हमेशा बेहतर होता है कि इंट्रेंसिक्स को संदर्भ द्वारा मान से बेहतर करें यदि वे संशोधित नहीं होते हैं और फ़ंक्शन द्वारा वापस आ जाते हैं।
संदर्भों का एक और दिलचस्प उपयोग उपयोगकर्ता-परिभाषित प्रकार के डिफ़ॉल्ट तर्क की आपूर्ति करना है:
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;
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
मुझे लगता है कि अभी तक एक और बिंदु है जो यहां कवर नहीं किया गया है।
बिंदुओं के विपरीत, संदर्भ, उनके द्वारा संदर्भित वस्तु के समानार्थक रूप से समतुल्य होते हैं, अर्थात कोई भी ऑपरेशन जो किसी संदर्भ के लिए किसी वस्तु के लिए लागू किया जा सकता है, और सटीक समान सिंटैक्स के साथ (अपवाद निश्चित रूप से आरंभीकरण है)।
हालांकि यह सतही दिखाई दे सकता है, मेरा मानना है कि यह संपत्ति कई सी ++ सुविधाओं के लिए महत्वपूर्ण है, उदाहरण के लिए:
टेंपरेचर । चूंकि टेम्प्लेट पैरामीटर डक-टाइप किए गए हैं, एक प्रकार के सिंटैक्टिक गुण सभी मायने रखते हैं, इसलिए अक्सर एक ही टेम्प्लेट का उपयोग दोनों के साथ किया जा सकता है T
और T&
।
(या std::reference_wrapper<T>
जो अभी भी एक निहित कलाकारों पर निर्भर है T&
)
दोनों को कवर करने वाले टेम्पलेट T&
और T&&
भी अधिक सामान्य हैं।
लैवल्स । कथन पर विचार करें str[0] = 'X';
संदर्भ के बिना यह केवल सी-स्ट्रिंग्स ( char* str
) के लिए काम करेगा । संदर्भ द्वारा वर्ण लौटाने से उपयोगकर्ता-परिभाषित कक्षाएं समान संकेतन की अनुमति देती हैं।
कंस्ट्रक्टरों को कॉपी करें । सिंथेटिक रूप से यह समझ में आता है कि ऑब्जेक्ट को कंस्ट्रक्टरों को कॉपी करना है, न कि ऑब्जेक्ट्स को पॉइंटर्स। लेकिन किसी कॉपी कंस्ट्रक्टर के लिए किसी ऑब्जेक्ट को वैल्यू द्वारा लेने का कोई तरीका नहीं है - इसके परिणामस्वरूप एक ही कॉपी करने वाले के लिए रिकर्सिव कॉल होगा। यह यहां एकमात्र विकल्प के रूप में संदर्भ छोड़ता है।
संचालक ओवरलोड है । संदर्भों के साथ एक ऑपरेटर कॉल को अप्रत्यक्ष रूप से पेश करना संभव है - operator+(const T& a, const T& b)
एक ही इन्फिक्स नोटेशन को बनाए रखते हुए। यह नियमित ओवरलोड कार्यों के लिए भी काम करता है।
ये बिंदु C ++ और मानक पुस्तकालय के काफी हिस्से को सशक्त करते हैं इसलिए यह संदर्भों की एक प्रमुख संपत्ति है।
पॉइंटर्स और रेफरेंस के बीच एक बहुत ही महत्वपूर्ण गैर-तकनीकी अंतर है: पॉइंटर द्वारा एक फ़ंक्शन को दिया गया एक तर्क गैर-कॉस्ट-रेफरेंस द्वारा एक फ़ंक्शन को दिए गए तर्क की तुलना में बहुत अधिक दिखाई देता है। उदाहरण के लिए:
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 ++ स्टाइल दिशानिर्देश इसका एक उदाहरण है।
शायद कुछ रूपकों से मदद मिलेगी; आपके डेस्कटॉप स्क्रीनस्पेस के संदर्भ में -
एक पॉइंटर को 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 "="
const int& i = 0
।
अंतर यह है कि गैर-स्थिर पॉइंटर वैरिएबल (निरंतर के लिए पॉइंटर के साथ भ्रमित नहीं होना) कार्यक्रम के निष्पादन के दौरान कुछ समय में बदला जा सकता है, पॉइंटर शब्दार्थ का उपयोग करने की आवश्यकता होती है (और, *) ऑपरेटर, जबकि संदर्भ आरंभीकरण पर सेट किए जा सकते हैं। केवल (इसीलिए आप उन्हें केवल कंस्ट्रक्टर इनिशियलाइज़र लिस्ट में सेट कर सकते हैं, लेकिन किसी और तरह से नहीं) और सिमेंटिक एक्सेस करने वाले साधारण मूल्य का उपयोग करें। मूल रूप से संदर्भ ओवरलोडिंग के लिए ऑपरेटरों को समर्थन देने की अनुमति देने के लिए पेश किए गए थे जैसा कि मैंने कुछ बहुत पुरानी किताब में पढ़ा था। जैसा कि इस धागे में किसी ने कहा है - पॉइंटर को 0 पर सेट किया जा सकता है या आप जो भी मूल्य चाहते हैं। 0 (NULL, nullptr) का अर्थ है कि सूचक कुछ भी नहीं के साथ आरंभीकृत है। यह एक त्रुटि को इंगित करता है। लेकिन वास्तव में पॉइंटर में एक मान हो सकता है जो कुछ सही मेमोरी लोकेशन की ओर इशारा नहीं करता है। अपनी बारी में संदर्भ उपयोगकर्ता को इस बात के संदर्भ में अनुमति देने की अनुमति नहीं देते हैं कि इस तथ्य के कारण इसे संदर्भित नहीं किया जा सकता है कि आप हमेशा इसे सही प्रकार का प्रदान करते हैं। यद्यपि संदर्भ चर बनाने के लिए बहुत सारे तरीके हैं, जिन्हें गलत मेमोरी स्थान पर आरंभीकृत किया जाता है - आपके लिए बेहतर है कि आप इस डीप को विवरण में न खोएं। मशीन स्तर पर सूचक और संदर्भ दोनों समान रूप से काम करते हैं - संकेत के माध्यम से। मान लें कि आवश्यक संदर्भों में वाक्य रचना चीनी है। rvalue के संदर्भ इसके लिए अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं। यद्यपि संदर्भ चर बनाने के लिए बहुत सारे तरीके हैं, जिन्हें गलत मेमोरी स्थान पर आरंभीकृत किया जाता है - आपके लिए बेहतर है कि आप इस डीप को विवरण में न खोएं। मशीन स्तर पर सूचक और संदर्भ दोनों समान रूप से काम करते हैं - संकेत के माध्यम से। मान लें कि आवश्यक संदर्भों में वाक्य रचना चीनी है। rvalue के संदर्भ इसके लिए अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं। यद्यपि संदर्भ चर बनाने के लिए बहुत सारे तरीके हैं, जिन्हें गलत मेमोरी स्थान पर आरंभीकृत किया जाता है - आपके लिए बेहतर है कि आप इस डीप को विवरण में न खोएं। मशीन स्तर पर सूचक और संदर्भ दोनों समान रूप से काम करते हैं - संकेत के माध्यम से। मान लें कि आवश्यक संदर्भों में वाक्य रचना चीनी है। rvalue के संदर्भ इसके लिए अलग हैं - वे स्वाभाविक रूप से ढेर / ढेर वस्तुओं हैं।
सरल शब्दों में, हम कह सकते हैं कि एक संदर्भ एक चर के लिए एक वैकल्पिक नाम है जबकि, एक सूचक एक चर है जो दूसरे चर का पता रखता है। जैसे
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 */