2010-08-09 4 views
26

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

क्या यह है?

स्पष्ट करने के लिए ...

MyType *pObj = ... 
MyType &obj = *pObj; 

, इस 'गंदा' नहीं है के बाद से आप कर सकते हैं (भले ही केवल सिद्धांत के बाद से आप इसे पहले जांच होगी में) एक NULL संकेतक भिन्नता?

संपादित करें: ओह, और आप नहीं जानते कि वस्तुओं को गतिशील रूप से बनाया गया था या नहीं।

+1

मैं कहूंगा कि आप जो भी सूचक चाहते हैं उसे डिफरेंस कर सकते हैं लेकिन मुझे यकीन नहीं है कि ऐसा कोई कार्य क्या करेगा। –

+0

@ जॉन: http://www.youtube.com/watch?v=sf-5RaFnh2U#t=2m14s :) –

+0

@ जॉन: क्या आप हमें बता सकते हैं कि जिन वस्तुओं को आप पॉइंटर्स संग्रहीत कर रहे हैं वे गतिशील रूप से आवंटित किए गए हैं? मुझे लगता है कि यह मामला था, लेकिन एक पूर्ण आवश्यकता नहीं है। –

उत्तर

4

नहीं। आप operator= को और कैसे कार्यान्वित कर सकते हैं? अपने आप को संदर्भ वापस करने के लिए आपको this को हटाना होगा।

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

+0

@ बिली: शायद आप उपर के बारे में सही हैं। ऐसा कहकर, कभी-कभी ऐसी वस्तुएं होती हैं जिन्हें कॉपी नहीं किया जा सकता है। –

+0

क्या ओवरहेड इस बात पर निर्भर नहीं करेगा कि आप किस प्रकार के कंटेनर का उपयोग कर रहे हैं? उदाहरण के लिए, एक 'std :: vector' खंडों में स्मृति को सुरक्षित रखता है और आपके द्वारा जोड़े गए प्रत्येक तत्व के लिए अलग आवंटन नहीं करता है। एक 'std :: set' या' std :: map' कार्यान्वयन प्रत्येक तत्व के लिए एक अलग आवंटन को बहुत अच्छी तरह से निष्पादित कर सकता है, और इस प्रकार आप जिस ओवरहेड के बारे में बात करते हैं उसे ले सकते हैं। या शायद कुछ और है जो मैं विचार नहीं कर रहा हूं। कृपया विस्तार से बताएं। –

+0

@ ए।लेवी: एक 'वेक्टर' आवश्यकतानुसार पुन: आवंटित करेगा, पुराने बफर से नए में उदाहरणों की प्रतिलिपि बनायेगा। लेकिन, हां, यह इन स्थानों में प्रतियों को तत्काल करने के लिए एक संगत सीमा आवंटित करेगा और 'नया' प्लेसमेंट का उपयोग करेगा। एक मानचित्र को प्रत्येक नोड के लिए एक ब्लॉक की आवश्यकता होगी, लेकिन फिर, यह कभी भी नोड की प्रतिलिपि बनाने की संभावना नहीं है। –

16

यह सुनिश्चित करें कि पॉइंटर संदर्भ में कनवर्ट करने का प्रयास करने से पहले पॉइंटर न्यूल नहीं है, और जब तक आपका संदर्भ (या ढेर के संदर्भ में आवंटित रहता है) तब तक ऑब्जेक्ट दायरे में रहेगा, और आप ठीक रहेगा, और नैतिक रूप से साफ होगा :)

+1

गतिशील रूप से आवंटित वस्तु कैसे दायरे में हो सकती है? –

+0

शून्य संदर्भ (असुविधाजनक) अपरिभाषित हैं। –

+2

@ बिली: हम नहीं जानते कि वे गतिशील रूप से आवंटित हैं, बस हम उन्हें इंगित कर रहे हैं। –

1

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

+1

'unique_ptr' केवल सी ++ 0x में उपलब्ध है, जो निषिद्ध हो सकता है। –

+0

@ बिली: "या जो भी समान प्रकार उपयुक्त है"। हमारे पास अभी 'auto_ptr' है, लेकिन बूस्ट कुछ बेहतर विकल्प प्रदान करता है। –

+3

@ स्टेवेन: 'auto_ptr' एसटीएल कंटेनरों के अंदर संग्रहीत नहीं किया जा सकता है। –

12

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

तो उदाहरण के लिए, मान लीजिए कि मुझे ऑब्जेक्ट्स की एक सरणी में पॉइंटर पास किया गया है। यह ऑब्जेक्टर जोड़ी, या ऑब्जेक्ट्स का वेक्टर, या map ऑब्जेक्ट्स के साथ ही हो सकता है, लेकिन मैं सादगी के लिए एक सरणी का उपयोग करूंगा। प्रत्येक ऑब्जेक्ट में एक फ़ंक्शन होता है, order, एक पूर्णांक लौटाता है। मैं bar समारोह एक बार बढ़ती order मूल्य की प्रत्येक वस्तु पर कॉल करने के लिए कर रहा हूँ:

void bar(Foo &f) { 
    // does something 
} 

bool by_order(Foo *lhs, Foo *rhs) { 
    return lhs->order() < rhs->order(); 
} 

void call_bar_in_order(Foo *array, int count) { 
    std::vector<Foo*> vec(count); // vector of pointers 
    for (int i = 0; i < count; ++i) vec[i] = &(array[i]); 
    std::sort(vec.begin(), vec.end(), by_order); 
    for (int i = 0; i < count; ++i) bar(*vec[i]); 
} 

संदर्भ है कि मेरे उदाहरण प्रारंभ हो जाता है एक समारोह पैरामीटर के बजाय सीधे एक चर रहा है, लेकिन मैं सिर्फ वैध किया जा सकता है :

for (int i = 0; i < count; ++i) { 
    Foo &f = *vec[i]; 
    bar(f); 
} 

जाहिर है एक vector<Foo>, गलत हो के बाद से तो मैं bar प्रत्येक वस्तु के एक प्रतिलिपि पर क्रम में प्रत्येक वस्तु पर क्रम में बुला लिया जाएगा, नहीं होता। bar एक गैर-कॉन्स्ट संदर्भ लेता है, इसलिए प्रदर्शन या कुछ और से अलग, यह स्पष्ट रूप से गलत होगा अगर bar इनपुट को संशोधित करता है।

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

+0

ठीक है, कभी-कभी आप केवल कच्चे सूचक चाहते हैं, जिससे स्वामित्व को कहीं और से निपटने की इजाजत मिलती है। –

+0

और यदि वेक्टर का दायरा सुरक्षित रूप से कुछ फ़ंक्शन के अंदर है जहां संदर्भ सभी मान्य हैं, तो कोई भी चोट नहीं पहुंचाता है ... –

2

मेरा उत्तर सीधे आपकी प्रारंभिक चिंता का समाधान नहीं करता है, लेकिन ऐसा लगता है कि आपको यह समस्या आती है क्योंकि आपके पास एक एसटीएल कंटेनर है जो पॉइंटर प्रकार संग्रहीत करता है।

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

इस धारणा को प्रदर्शित करने के लिए यहां एक त्वरित उदाहरण है।

#include <string> 
#include <boost/ptr_container/ptr_vector.hpp> 

void foo() 
{ 
    boost::ptr_vector<std::string> strings; 

    strings.push_back(new std::string("hello world!")); 
    strings.push_back(new std::string()); 

    const std::string& helloWorld(strings[0]); 
    std::string& empty(strings[1]); 
} 
+0

अच्छा। स्मार्ट पॉइंटर प्रभावी ढंग से बनाया गया है, लेकिन कंटेनर संदर्भ अर्थशास्त्र प्रदान करता है। –

1

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

बेशक क्या होता है वास्तव में संकलन संस्करण और हार्डवेयर प्लेटफॉर्म के साथ-साथ संकलक विकल्प और संदर्भ के सटीक उपयोग पर निर्भर करता है।

आधिकारिक तौर पर 0-पॉइंटर को संदर्भित करने का व्यवहार अपरिभाषित है और इस प्रकार कुछ भी हो सकता है। यह कुछ भी में यह भी शामिल है कि यह तुरंत क्रैश हो सकता है, लेकिन यह भी कि बाद में या कभी भी क्रैश हो सकता है।

तो हमेशा यह सुनिश्चित करें कि आप किसी संदर्भ में 0-पॉइंटर निर्दिष्ट न करें - बग पसंद करते हैं कि यह ढूंढना बहुत मुश्किल है।

संपादित करें: "आमतौर पर" इटालिक और आधिकारिक "अपरिभाषित" व्यवहार के बारे में अतिरिक्त अनुच्छेद बनाया गया।

+2

हम्म, सी ++ मानक * की आवश्यकता है * संदर्भ सीधे संकेतक के रूप में लागू किया जाना चाहिए? –

+0

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

2

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

बस स्पष्ट होने के लिए: एसटीएल कंटेनरों को कुछ अर्थशास्त्र ("मूल्य अर्थशास्त्र") का समर्थन करने के लिए डिज़ाइन किया गया था, जैसे "कंटेनर में आइटम की प्रतिलिपि बनाई जा सकती है।" चूंकि संदर्भ पुनर्निर्मित नहीं हैं, इसलिए वे मूल्य semantics का समर्थन नहीं करते हैं (यानी, std::vector<int&> या std::list<double&> बनाने का प्रयास करें)। आप सही हैं कि आप एसटीएल कंटेनर में संदर्भ नहीं डाल सकते हैं।

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

#include <iostream> 
#include <vector> 

// note signature, inside this function, i is an int& 
// normally I would pass a const reference, but you can't add 
// a "const* int" to a "std::vector<int*>" 
void add_to_vector(std::vector<int*>& v, int& i) 
{ 
    v.push_back(&i); 
} 

int main() 
{ 
    int x = 5; 
    std::vector<int*> pointers_to_ints; 

    // x is passed by reference 
    // NOTE: this line could have simply been "pointers_to_ints.push_back(&x)" 
    // I simply wanted to demonstrate (in the body of add_to_vector) that 
    // taking the address of a reference returns the address of the object the 
    // reference refers to. 
    add_to_vector(pointers_to_ints, x); 

    // get the pointer to x out of the container 
    int* pointer_to_x = pointers_to_ints[0]; 

    // dereference the pointer and initialize a reference with it 
    int& ref_to_x = *pointer_to_x; 

    // use the reference to change the original value (in this case, to change x) 
    ref_to_x = 42; 

    // show that x changed 
    std::cout << x << '\n'; 
} 

ओह, और आप अगर वस्तुओं गतिशील बनाए गए थे या नहीं पता नहीं है:

और, हाँ, निम्नलिखित कानूनी (हालांकि इस मामले में, बहुत उपयोगी नहीं में) है।

यह महत्वपूर्ण नहीं है। उपर्युक्त नमूने में, x स्टैक पर है और हम में x पर एक पॉइंटर स्टोर करते हैं। निश्चित रूप से, pointers_to_vectors आंतरिक रूप से गतिशील रूप से आवंटित सरणी का उपयोग करता है (और delete[] वह सरणी जब vector गुंजाइश से बाहर हो जाता है), लेकिन उस सरणी में पॉइंटर्स होते हैं, न कि चीजों को इंगित करते हैं। जब pointers_to_ints गुंजाइश से बाहर हो जाता है, तो आंतरिक int*[]delete[] -ed है, लेकिन int* एस delete डी नहीं हैं।

यह वास्तव में, एसटीएल कंटेनरों के साथ पॉइंटर्स का उपयोग करता है, क्योंकि एसटीएल कंटेनर पॉइंट-ऑब्जेक्ट्स के जीवनकाल का प्रबंधन नहीं करेंगे। आप बूस्ट के सूचक कंटेनर लाइब्रेरी को देखना चाह सकते हैं। अन्यथा, आप या तो (1) स्मार्ट पॉइंटर्स के एसटीएल कंटेनरों का उपयोग करना चाहते हैं (जैसे boost:shared_ptr जो एसटीएल कंटेनर के लिए कानूनी है) या (2) ऑब्जेक्ट्स के जीवनकाल को किसी अन्य तरीके से प्रबंधित करें। आप पहले से ही कर रहे हैं (2)।

संबंधित मुद्दे