2015-09-25 19 views
6

मुझे एक सेगफॉल्ट के साथ कोई समस्या है जिसे मैं समझ नहीं सकता। यह एक छोटे से गेम इंजन के लिए EntityManager से है जिस पर मैं काम कर रहा हूं। मैं Ship Entity जोड़ सकता हूं, और शिप 1 Bullet Entity जोड़ सकता है, लेकिन अगर मैं 1 से अधिक Bullet जोड़ने का प्रयास करता हूं तो यह segfaults है। मैं इसे पिछले दिन के लिए बाहर निकालने की कोशिश कर रहा हूं। नीचे वास्तविक कोड से एक छोटा सा अंश है।अद्वितीय_ptr हटाने का वेक्टर?

#include <vector> 
#include <memory> 

struct EntityManager; 
struct Entity { 
    Entity(EntityManager* manager) : manager(manager) { } 
    virtual ~Entity() { } 
    virtual void update() = 0; 

    EntityManager* manager; 
}; 
struct EntityManager { 
    void update() { 
     for (auto& entity : entities) { 
      entity->update(); 
     } 
    } 
    void add(Entity* e) { 
     entities.emplace_back(e); 
    } 
    std::vector<std::unique_ptr<Entity>> entities; 
}; 
struct Bullet : public Entity { 
    Bullet(EntityManager* manager) : Entity(manager) { printf("Bullet ctor\n"); } 

    virtual void update() override { } 
}; 
struct Ship : public Entity { 
    Ship(EntityManager* manager) : Entity(manager) { } 

    virtual void update() override { 
     printf("Adding Bullet\n"); 
     manager->add(new Bullet(manager)); 
    } 
}; 
int main() { 
    EntityManager manager; 
    manager.add(new Ship(&manager)); 

    int loops{0}; 
    while (loops < 100) { 
     manager.update(); 
     loops++; 
     printf("Completed Loop #%d\n", loops); 
    } 
    return 0; 
} 

वास्तविक कोड में, सब कुछ अपने स्वयं के ज/सीपीपी फ़ाइलों में है, और structs के बजाय कक्षाएं, लेकिन मुद्दा एक ही है। उत्पादन `जोड़ना है बुलेट // गोली ctor // 1 // गोली जोड़ना // गोली ctor // सिग्नल पूरे लूप #: SIGSEGV (विभाजन गलती)

segfault entity->update(); लाइन पर EntityManager::update() में होता है।

for (auto& entity : entities) { 
     entity->update(); 
    } 

आप इसे माध्यम से व्यस्त बार-बार दोहराना जब आप एक नया तत्व है, जो iterators कंटेनर पार करने के लिए इस्तेमाल किया जा रहा अमान्य कर जोड़ने के लिए वेक्टर को संशोधित कर रहे हैं:

+1

'EntityManager' को' 'प्रबंधक' सूचक को अद्यतन करने के लिए कस्टम चाल संचालन की आवश्यकता है। – dyp

+1

जब आप संस्थाओं को अद्यतन करते हैं, तो वह लूप अधिक इकाइयों को जोड़ता है जो आपके इटरेटर को अमान्य करता है। आप अपने वेक्टर में जोड़ नहीं सकते हैं, जबकि आप इसके माध्यम से लूपिंग के बीच में हैं। – Galik

+1

[गेम इंजन नहीं लिखें] (http://geometrian.com/programming/tutorials/write-games-not-engines/) पढ़ने के लिए एक अच्छी बात है। उस ने कहा, चीजों को तत्काल जोड़ने या तुरंत चीजों को नष्ट करने के बजाय, आप उन परिचालनों को अपडेट लूप के बाद, घटनाओं या जो कुछ भी उचित पाते हैं, के बाद देरी कर सकते हैं, ताकि आप इसे इटरेटर्स को अमान्य नहीं कर सकें। – aslg

उत्तर

13

समस्या यह है कि इस पाश वेक्टर को संशोधित करता है।

रेंज आधारित for पाश करने के लिए संकलक द्वारा विस्तार किया जाता है:

auto begin = entities.begin(), end = entities.end(); 
for (; begin != end; ++begin) 
    begin->update(); 

begin->update() करने के लिए कॉल वेक्टर, जो कंटेनर में सभी iterators को अमान्य कर के लिए एक नया तत्व को जोड़ता है, तो ++begin अपरिभाषित व्यवहार है । व्यावहारिक रूप से, begin अब वेक्टर में इंगित नहीं करता है (क्योंकि यह पुरानी स्मृति को फिर से आवंटित और मुक्त कर दिया गया है जो begin इंगित करता है) ताकि अगले begin->update() कॉल को एक अमान्य इटरेटर को मुक्त कर दें, मुक्त स्मृति और सीजी-गलती तक पहुंचें।

यह ऐसा करने के लिए सुरक्षित रूप से आप शायद सूचकांकों का उपयोग करना चाहते iterators:

for (size_t i = 0, size = entities.size(); i != size; ++i) 
    entities[i].update(); 

इस पाश और अंतिम तत्व मौजूद न होने पर पाश शुरू होता है अप करने के लिए इतना ही दोहराता के शुरू में आकार लेती है, तो अंत में जोड़े गए नए तत्वों का दौरा नहीं किया जाएगा।

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

+0

मैंने अपने लूप को लूप के लिए एक निश्चित करने के लिए बदल दिया है जो एक इटरेटर (ऑटो इंडेक्स {0u}; इंडेक्स BFritz

+3

नहीं, यह सुरक्षित नहीं है। यह "लूप के लिए तय नहीं है" क्योंकि 'vec.size()' परिवर्तन, और यह हमेशा जोड़े जाने के रूप में नई इकाइयों का दौरा करने के लिए पाश कर सकता है। यह उपर्युक्त आपके उदाहरण के लिए काम करता है क्योंकि 'बुलेट' वेक्टर को संशोधित नहीं करता है, लेकिन आप पहले अपडेट लूप के दौरान दोनों इकाइयों पर जाते हैं। एक अच्छा कारण है कि मैंने सही तरीके से सही तरीके से लिखा था। जैसा कि मैंने कहा: _ "यह लूप की शुरुआत में आकार को कैप्चर करता है और इसलिए लूप प्रारंभ होने पर मौजूद अंतिम तत्व तक ही पुनरावृत्त होता है, इसलिए अंत में जोड़े गए नए तत्वों का दौरा नहीं किया जाएगा।" _ –

+0

आप सही हैं , कुछ परीक्षण चलाने के बाद मुझे पता चला कि यह ऊपर वर्णित तरीके से काम नहीं करता है। जैसा कि आपने अपने उत्तर में सुझाव दिया है, मैं पूर्व-कब्जे वाले चर का उपयोग करूंगा। मैं ईमानदारी से महसूस करता हूं कि मुझे पता होना चाहिए कि समस्या क्या थी। आपकी सहायताके लिए धन्यवाद! – BFritz

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