2010-02-12 10 views
7

मैं एक गेम बना रहा हूं और मेरे पास चारों ओर उड़ने वाली गोलियों का एक वेक्टर है। जब गोली समाप्त हो जाती है, तो मैं bullets.erase (bullets.begin() + i) करता हूं; तब गोली गायब हो जाती है। हालांकि यह स्मृति की छड़ी पाने के लिए नहीं है। अगर मैं 5000 गोलियां बनाता हूं तो उन मरने के बाद 5000 और अधिक बनाते हैं, मेमोरी वही रहता है, लेकिन अगर मैं 5000 और अधिक उड़ रहा हूं, तो 5000 और अधिक उड़ान भरने पर, यह नई जगह आवंटित करेगा। वास्तव में इस स्मृति को मुक्त करने के लिए मुझे क्या करना है?उचित वेक्टर मेमोरी मैनेजमेंट

धन्यवाद

+0

आप इसे कैसे कर रहे हैं? एसटीएल, बूस्ट, रोल-अप-खुद, या कुछ और? – John

+0

एसटीएल वेक्टर एक – jmasterx

+0

है आप आकार को कम करने के लिए 'std :: vector :: resize()' का उपयोग कर सकते हैं। Http://www.cplusplus.com/reference/stl/vector/resize –

उत्तर

14

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

std::vector में "आकार" की दो प्रासंगिक अवधारणाएं हैं। पहला "आरक्षित" आकार है, यह है कि वेक्टर तत्वों को संग्रहीत करने के लिए सिस्टम से कितनी मेमोरी आवंटित की गई है। दूसरा "प्रयुक्त" आकार है, यह है कि वेक्टर में कितने तत्व तर्कसंगत हैं। जाहिर है, आरक्षित आकार कम से कम इस्तेमाल आकार के रूप में बड़ा होना चाहिए। आप size() विधि (जो मुझे यकीन है कि आप पहले ही जानते हैं) के साथ उपयोग किए गए आकार की खोज कर सकते हैं, और आप capacity() विधि का उपयोग करके आरक्षित आकार की खोज कर सकते हैं।

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

दुर्भाग्य से, जब तुम वृद्धि अपने आरक्षित आकार reserve() विधि का उपयोग करने के लिए एक वेक्टर मजबूर कर सकते हैं, इस आरक्षित क्षमता कम करने के लिए काम नहीं करता। जहाँ तक मेरा बता सकते हैं, क्षमता में कमी प्रभावशाली के लिए आपका सर्वश्रेष्ठ दांव निम्नलिखित करना है:

std::vector<Bullet>(myVector).swap(myVector); 

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

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

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

+0

मध्य से महंगा होने के साथ-साथ इस उत्तर की व्यापकता के बारे में बिंदु के लिए +1। –

+1

'myVector.swap (std :: vector (myVector));' मान्य सी ++ 03 मान्य नहीं है, क्योंकि स्वैप संदर्भ द्वारा अपना तर्क लेता है, और कन्स्ट्रक्टर कॉल एक रैल्यू है। कोड अनुरूप बनाने के लिए पैरामीटर स्विच करें: 'std :: vector (myVector) .swap (myVector);' – fredoverflow

+0

@Fred धन्यवाद, आप पूरी तरह से सही हैं। मैंने इसे दूसरी तरफ लिखा क्योंकि ऐसा लगता है कि इस तरह से क्या चल रहा है, लेकिन उस चेतावनी के बारे में भूल गया। फिक्स्ड। –

2

यही कारण है कि कैसे सामान्य रूप से वेक्टर के स्मृति आवंटन मॉडल एक परिशोधित निरंतर समय push_back आपरेशन प्रदान करने के लिए व्यवहार करती है, मूल रूप से यह अनुमान लगाना कि आप एक नया तत्व के साथ मिट भाग को भरने के लिए चाहते हो सकता है इसलिए यह नहीं है की कोशिश करता है स्मृति मुक्त करें। ऐसा करने से यह निरंतर आवंटन और विध्वंस से बच सकता है। इसके आस-पास जाने के लिए, आप अप्रयुक्त वेक्टर मेमोरी को मुक्त करने के लिए स्वैप चाल का उपयोग कर सकते हैं। आपको अपने खाली वेक्टर को एक अस्थायी अज्ञात वेक्टर के साथ स्वैप करना होगा ताकि जब अस्थायी वेक्टर गुंजाइश से बाहर हो जाए, तो यह स्मृति को अपने विनाशक में मुक्त करता है, कुछ ऐसा: vector<int>(c).swap(c)

+0

यह थोड़ा हैशिश लगता है – jmasterx

+0

यह एक प्रसिद्ध हैक है। डिजाइन द्वारा, वैक्टर खुद को कम नहीं करते हैं। –

+0

क्या कोई ऐसा स्टॉल है जो मैं चाहता हूं? – jmasterx

3

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

दूसरा, वास्तविक समस्या क्या है - 10,000 वस्तुओं को हटाने के बाद, स्मृति मुक्त नहीं होती है? क्या हम ऑपरेटिंग सिस्टम द्वारा रिपोर्ट की गई मुफ्त मेमोरी, या ढेर पर खाली जगह के बारे में बात कर रहे हैं? यह संभव है (और बहुत संभावना है), कि वेक्टर के डेटा की स्थिति के बाद कुछ अन्य वस्तु आवंटित की गई थी, इसलिए इस स्मृति को ऑपरेटिंग सिस्टम में आसानी से मुक्त करना संभव नहीं है; लेकिन इसे अन्य, नव निर्मित वस्तुओं के लिए पुन: उपयोग किया जा सकता है।

2

मैं सुझाव है कि आप इन दो मुहावरों पर एक नज़र डालें और एक कि आप सबसे फिट बैठता है लेने के लिए:
Shrink to fit
Clear & minimize

+1

+1, याद रखना और संवाद करना आसान है। –

+0

धन्यवाद, यह पुस्तक बीटीडब्ल्यू सुनहरा है :) –

1

यह स्मृति से छुटकारा पाने के नहीं हो सकता है।
लेकिन अगली बार जब आपको एक बैलिट जोड़ने की आवश्यकता होती है तो इसे और अधिक स्थान आवंटित करने की आवश्यकता नहीं होती है।
यह उस स्मृति का पुन: उपयोग नहीं करेगा जो मिटाई गई गोली से आई थी।

नोट:
यदि आप अपेक्षाकृत अक्सर कंटेनर के बीच से मिट रहे हैं तो वेक्टर सही कंटेनर नहीं हो सकता है। यदि आप तत्व n को हटाते हैं तो यह becuase है, तो [n + 1, end) के सभी तत्वों को स्मृति में एक स्थान नीचे ले जाना चाहिए।

0

जब गोली समाप्त हो जाती है, तो मैं bullets.erase (bullets.begin() + i) करता हूं;

ऐसा मत करो। यदि प्रति फ्रेम कई गोलियां समाप्त हो जाती हैं, तो आपको भयानक प्रदर्शन मिलता है, क्योंकि गोलियां जो समाप्त नहीं होती हैं, उन्हें प्रतिलिपि मिल जाएगी, जो वास्तव में आवश्यक नहीं है। यहां मैं क्या करूँगा:

#include <algorithm> 
#include <functional> 
#include <vector> 

class Bullet 
{ 
    // ... 
public: 
    bool is_finished() const; 
}; 

int main() 
{ 
    std::vector<Bullet> bullets; 
    // ... 
    bullets.erase(
     std::remove_if(
      bullets.begin(), 
      bullets.end(), 
      std::mem_fun_ref(&Bullet::is_finished) 
     ), 
     bullets.end() 
    ); 
} 

यह दृष्टिकोण केवल एक बार में प्रत्येक लाइव बुलेट को स्थानांतरित करता है।

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