क्या मुझे कंटेनर में संपूर्ण ऑब्जेक्ट या पॉइंटर्स को ऑब्जेक्ट में संग्रहीत करना चाहिए?


162

खरोंच से एक नई प्रणाली डिजाइन करना। मैं कुछ लंबी-जीवित वस्तुओं की सूचियों और मानचित्रों को संग्रहीत करने के लिए एसटीएल का उपयोग करूंगा।

प्रश्न: क्या मुझे यह सुनिश्चित करना चाहिए कि मेरे ऑब्जेक्ट्स में कॉपी कंस्ट्रक्टर हैं और अपने STL कंटेनरों के भीतर ऑब्जेक्ट्स की कॉपी स्टोर करें, या आम तौर पर जीवन का प्रबंधन करना बेहतर है और अपने STL कंटेनरों में केवल उन ऑब्जेक्ट्स को पॉइंटर्स स्टोर करना है?

मुझे पता है कि यह विवरण पर कुछ हद तक कम है, लेकिन मैं "सैद्धांतिक" बेहतर उत्तर की तलाश कर रहा हूं यदि यह मौजूद है, क्योंकि मुझे पता है कि ये दोनों समाधान संभव हैं।

संकेत के साथ खेलने के लिए दो बहुत स्पष्ट नुकसान: 1) मुझे एसटीएल से परे एक दायरे में खुद इन वस्तुओं के आवंटन / निपटान का प्रबंधन करना चाहिए। 2) मैं ढेर पर एक अस्थायी वस्तु नहीं बना सकता और इसे अपने कंटेनरों में जोड़ सकता हूं।

क्या मुझे और कुछ याद आ रहा है?


36
भगवान मुझे इस साइट से प्यार है, यह आज का सवाल है जो मैं सोच रहा था ... मेरे लिए यह पूछने का काम करने के लिए धन्यवाद :-)
दुष्ट

2
एक और दिलचस्प बात यह है कि हमें यह जांचना चाहिए कि क्या सूचक वास्तव में संग्रह में जोड़ा गया था और अगर यह संभव नहीं है कि हम मेमोरी लीक से बचने के लिए डिलीट को कॉल करें ... यदि ((set.insert (पॉइंटर))। दूसरा = गलत)। {डिलीट पॉइंटर;}
javapowered

जवाबों:


68

चूँकि लोग पॉइंटर्स का उपयोग करने की दक्षता पर चिंतन कर रहे हैं।

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

ओटोह, अगर अपडेट सामान्य भंडारण बिंदु हैं तो प्रतिलिपि / स्थानांतरण लागतों को बचाएगा।


7
कैश लोकलिटी के संदर्भ में, वेक्टर में स्टोरिंग पॉइंटर्स कुशल हो सकते हैं यदि पॉइंटर के लिए कस्टम एलोकेटर के साथ उपयोग किया जाए। कस्टम आवंटनकर्ता को कैश लोकलिटी का ध्यान रखना होता है, उदाहरण के लिए प्लेसमेंट नए ( en.wikipedia.org/wiki/Placement_syntax#Custom_allocators ) देखें ।
अमित

47

यह वास्तव में आपकी स्थिति पर निर्भर करता है।

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

यदि आप ऑब्जेक्ट बड़े हैं, और डिफॉल्ट कंस्ट्रक्टर होने का कोई मतलब नहीं है, या ऑब्जेक्ट्स की प्रतियां महंगी हैं, तो पॉइंटर्स के साथ स्टोर करना संभवतः जाने का तरीका है।

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

प्रत्येक पॉइंटर कंटेनर (उदाहरण के लिए ptr_vector) एक वस्तु का स्वामित्व लेता है जब इसे कंटेनर में जोड़ा जाता है, और आपके लिए उन वस्तुओं के जीवनकाल का प्रबंधन करता है। आप संदर्भ द्वारा एक ptr_ कंटेनर में सभी तत्वों को भी एक्सेस करते हैं। इससे आप जैसे काम कर सकते हैं

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

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

कंटेनर में एक पॉइंटर के स्वामित्व को कॉलर में स्थानांतरित करने की सुविधा भी है (अधिकांश कंटेनरों में रिलीज़ फ़ंक्शन के माध्यम से)।


38

यदि आप बहुरूपी वस्तुओं का भंडारण कर रहे हैं तो आपको हमेशा बेस क्लास पॉइंटर्स के संग्रह का उपयोग करने की आवश्यकता होती है।

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


1
मैं टुकड़ा करने की क्रिया से प्यार करता था!
idichekop

22

घटना के 3 साल बाद कूदने के लिए क्षमा करें, लेकिन यहां एक सावधानी ...

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

मेरी वर्तमान बड़ी परियोजना पर, मेरी केंद्रीय डेटा संरचना काफी सीधी वस्तुओं का एक सेट है। परियोजना में लगभग एक वर्ष (जो आज होता है), मैंने महसूस किया कि वस्तु को वास्तव में बहुरूपी होना चाहिए। नेट पर वापस, यह धागा मिला, और निक को बूस्ट पॉइंटर कंटेनर लाइब्रेरी के लिंक से मिला। यह वही है जो मुझे सब कुछ ठीक करने के लिए पिछली बार लिखना था, इसलिए मैं इसे इस बार चारों ओर दे दूँगा।

मेरे लिए, वैसे भी नैतिक: यदि आपकी कल्पना पत्थर में 100% नहीं है, तो संकेत के लिए जाएं, और आप संभवतः बाद में अपने आप को बहुत काम बचा सकते हैं।


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

आप मूल्य शब्दार्थ के साथ वेक्टर में आइटम को छोड़ सकते थे और अंदर बहुरूपी व्यवहार करते थे।
बिली ओनेल

19

दोनों दुनिया के सर्वश्रेष्ठ क्यों नहीं मिलते: स्मार्ट पॉइंटर्स (जैसे boost::shared_ptrया std::shared_ptr) का एक कंटेनर करें । आपको मेमोरी को प्रबंधित करने की आवश्यकता नहीं है, और आपको बड़े कॉपी ऑपरेशन से निपटने की आवश्यकता नहीं है।


बूस्ट पॉइंटर कंटेनर लाइब्रेरी का उपयोग करके निक हदद ने जो सुझाव दिया है, उससे यह दृष्टिकोण अलग कैसे है?
थोरस्टोन शूइंग

10
@ ThorstenSchöning std :: share_ptr बढ़ावा देने पर निर्भरता नहीं जोड़ता है।
जेम्स जॉनसन

आप अपने बहुरूपता को संभालने के लिए साझा किए गए पॉइंटर्स का उपयोग नहीं कर सकते हैं ताकि आप अंततः इस दृष्टिकोण के साथ इस सुविधा को याद कर सकें, जब तक कि आप स्पष्ट रूप से संकेत नहीं देते हैं
auserdude

11

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

यदि आपकी वस्तु में स्वयं गैर-प्रतिलिपि योग्य वाक्यविन्यास है या एक सार आधार प्रकार है तो आपको पॉइंटर्स स्टोर करने की आवश्यकता होगी (सबसे आसान है share_ptr का उपयोग करना)


4
यदि आपकी वस्तुएं बड़ी हैं, तो यह सबसे अधिक कारगर नहीं है, और आप तत्वों को अक्सर घुमाते हैं।
एलियोरकोड

3

आपको अंतर की अच्छी समझ है। यदि ऑब्जेक्ट छोटे और कॉपी करने में आसान हैं, तो हर तरह से उन्हें स्टोर करें।

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

@ Torbjörn टुकड़ा करने की क्रिया के बारे में एक अच्छा बिंदु बनाता है।


1
ओह, और कभी भी कभी भी auto_ptr का संग्रह बनाएं
Torbjörn Gyllebring

दाईं ओर, auto_ptr एक स्मार्ट पॉइंटर नहीं है - यह गिनती को रिफ नहीं करता है।
लू फ्रेंको

auto_ptr में गैर-विनाशकारी प्रतिलिपि शब्दार्थ नहीं होता है, या तो। से एक auto_ptr का काम करने का कार्य oneकरने के लिए anotherसे संदर्भ जारी करेंगे oneऔर परिवर्तन one
एंडी फिन्केनस्टैड

3

पॉइंटर्स का उपयोग करना अधिक कुशल होगा क्योंकि कंटेनर केवल पूर्ण ऑब्जेक्ट्स के बजाय चारों ओर पॉइंटर्स की नकल करेंगे।

एसटीएल कंटेनर और स्मार्ट पॉइंटर्स के बारे में कुछ उपयोगी जानकारी यहाँ दी गई है:

मानक कंटेनरों के साथ std :: auto_ptr <> का उपयोग करना क्यों गलत है?


2

यदि वस्तुओं को कोड में कहीं और भेजा जाना है, तो बढ़ावा देने के वेक्टर में स्टोर करें :: साझा किया गया। यह सुनिश्चित करता है कि यदि आप वेक्टर का आकार बदलते हैं, तो ऑब्जेक्ट को पॉइंटर्स मान्य रहेगा।

अर्थात:

std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

यदि कोई और वस्तुओं की ओर संकेत नहीं करता है, या सूची बढ़ती नहीं है और सिकुड़ती है, तो सीधे सादे-पुरानी वस्तुओं के रूप में संग्रहीत करें:

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol

1

यह सवाल मुझे कुछ समय के लिए परेशान कर रहा है।

मैं स्टोरिंग पॉइंटर्स पर झुक जाता हूं, लेकिन मेरे पास कुछ अतिरिक्त आवश्यकताएं हैं (SWIG lua रैपर) जो आपके लिए लागू नहीं हो सकती हैं।

इस पोस्ट में सबसे महत्वपूर्ण बिंदु यह है कि आप अपनी वस्तुओं का उपयोग करते हुए, स्वयं इसका परीक्षण करें

मैंने आज 10 मिलियन ऑब्जेक्ट्स के संग्रह पर एक सदस्य फ़ंक्शन को कॉल करने की गति का परीक्षण करने के लिए 500 बार किया।

Xdir और ydir (सभी फ्लोट सदस्य चर) के आधार पर फ़ंक्शन x और y को अपडेट करता है।

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

संदर्भ के लिए, मेरे हार्डवेयर पर -O3 के साथ पॉइंटर्स को पूरा होने में 41 सेकंड लगे और कच्ची वस्तुओं को पूरा होने में 30 सेकंड का समय लगा।

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