2013-01-08 23 views
5

मैंने एक सरल Trie कार्यान्वयन लिखा है।नया नहीं कहा जाता है, फिर भी स्मृति आवंटित

#include <iostream> 
#include <sstream> 
#include "Trie.h" 

int main() { 
    Trie t; 
    for (unsigned int i = 0; i < 10000; ++i) { 
      t.insert("hello"); 
    } 
    return 0; 
} 

मेरे समस्या यह है कि भले ही 'हैलो' पहले से ही दूसरी बार डाला जाता है इस प्रविष्टि का प्रयास किया है, और इस तरह new है:

#include <string> 
#include <map> 

typedef unsigned int uint; 

class Trie { 
public: 
    class Node { 
    public: 
      Node(const char & _value); 
      ~Node(); 
      char get_value() const; 
      void set_marker(const uint & _marker); 
      uint get_marker() const; 
      bool add_child(Node * _child); 
      Node * get_child(const char & _value) const; 
      void clear(); 
    private: 
      char m_value; 
      uint m_marker; 
      std::map<char, Node *> m_children; 
    }; 

    Trie(); 
    ~Trie(); 
    bool insert(const std::string & _str); 
    bool find(const std::string & _str) const; 
private: 
    Node * m_root; 
}; 
// - implementation (in a different file) 
using namespace std; 

Trie::Node::Node(const char & _value) : 
      m_value(_value), m_marker(0), m_children() { 
} 

Trie::Node::~Node() { 
    clear(); 
} 

void Trie::Node::clear() { 
    map<char, Node*>::const_iterator it; 
    for (it = m_children.begin(); it != m_children.end(); ++it) { 
      delete it->second; 
    } 
} 

void Trie::Node::set_marker(const uint & _marker) { 
    m_marker = _marker; 
} 

uint Trie::Node::get_marker() const { 
    return m_marker; 
} 

char Trie::Node::get_value() const { 
    return m_value; 
} 

Trie::Node * Trie::Node::get_child(const char & _value) const { 
    map<char, Node*>::const_iterator it; 
    bool found = false; 
    for (it = m_children.begin(); it != m_children.end(); ++it) { 
      if (it->first == _value) { 
        found = true; 
        break; 
      } 
    } 
    if (found) { 
      return it->second; 
    } 
    return NULL; 
} 

bool Trie::Node::add_child(Node * _child) { 
    if (_child == NULL) { 
      return false; 
    } 
    if (get_child(_child->get_value()) != NULL) { 
      return false; 
    } 
    m_children.insert(pair<char, Node *>(_child->get_value(), _child)); 
    return true; 
} 

Trie::Trie() : 
      m_root(new Node('\0')) { 
} 

Trie::~Trie() { 
    delete m_root; 
} 

bool Trie::insert(const string & _str) { 
    Node * current = m_root; 
    bool inserted = false; 
    for (uint i = 0; i < _str.size(); ++i) { 
      Node * child = current->get_child(_str[i]); 
      if (child == NULL) { 
        child = new Node(_str[i]); 
        current->add_child(child); 
        inserted = true; 
      } 
      current = child; 
    } 
    if (current->get_marker() != _str.size()) { 
      current->set_marker(_str.size()); 
      inserted = true; 
    } 
    return inserted; 
} 

bool Trie::find(const std::string & _str) const { 
    Node * current = m_root; 
    bool found = false; 
    for (uint i = 0; i < _str.size(); ++i) { 
      Node * child = current->get_child(_str[i]); 
      if (child == NULL) { 
        break; 
      } else { 
        current = child; 
      } 
    } 
    if (current->get_marker() == _str.size()) { 
      found = true; 
    } 
    return found; 
} 

यहाँ मेरी परीक्षण कार्यक्रम है: यहाँ स्रोत कोड है अब और नहीं कहा जाता है, बहुत सारी मेमोरी आवंटित की जा रही है और आवंटित की जा रही है। यह राशि बढ़ जाती है क्योंकि मैं अधिकतम i के मूल्य को बढ़ाता हूं।

==10322== HEAP SUMMARY: 
==10322==  in use at exit: 0 bytes in 0 blocks 
==10322== total heap usage: 10,011 allocs, 10,011 frees, 300,576 bytes allocated 

मैं पुष्टि की है कि के समय नोड() निर्माता संख्या कहा जाता है स्थिर है: उदाहरण के लिए, उपरोक्त मामले valgrind में यह उत्पादन देता है। फिर क्यों और कैसे सभी स्मृति आवंटित और आवंटित किया जा रहा है?

+6

आप बहुत सारे मानचित्र बना रहे हैं। वे आंतरिक रूप से स्मृति आवंटित कर सकते हैं। –

उत्तर

13

हर एक बार जब आप insert कहते हैं, आप इसे एक const char[6] गुजरती हैं, लेकिन यह एक const std::string& उम्मीद है, और इसलिए प्रत्येक और हर यात्रा एक अस्थायी std::string है, जो तब कार्य करने के लिए पारित कर दिया, और फिर नष्ट हो जाता है अगले चरण से पहले पैदा करता है। यह आवंटन और विलोपन के 10000 को स्पष्ट करता है, जो केवल 11 को छोड़ देता है, जो संभवत: std::map आंतरिक रूप से करता है, और कुछ अन्य स्थानों को मैंने अनदेखा किया है (जैसे तारों या मानचित्र की प्रतियां)

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

5

std::map गतिशील रूप से अपनी याददाश्त आवंटित करेगा, और हर बार जब आप get_child() पर कॉल करते हैं तो आप एक नया बनाते हैं। डिफ़ॉल्ट कन्स्ट्रक्टर का उपयोग करते समय यह कितनी मेमोरी आवंटित करता है, मैं नहीं कह सकता, लेकिन शायद यह कुछ है। सिर्फ इसलिए कि आप new पर कॉल नहीं करते हैं इसका मतलब यह नहीं है कि आपकी कक्षा द्वारा बनाए गए अन्य प्रकार नहीं हैं।

इसके अलावा, std::map डालने वाले प्रत्येक तत्व के लिए पूरी तरह से नया ढेर स्टोर आवंटित नहीं किया जा रहा है। वह बहुत अक्षम होगा। इसकी आवश्यकता होने पर इसके बैकिंग स्टोर को बढ़ाने के लिए इसमें कुछ आंतरिक एल्गोरिदम है, और यह निश्चित रूप से उस नए तत्व को फिट करने के लिए आवश्यकतानुसार आवंटित होगा।

+0

क्या आप कृपया इसे और अधिक अच्छी तरह से पुष्टि कर सकते हैं? मैं बस संग्रहीतकर्ताओं के माध्यम से संग्रहीत 'std :: map' के माध्यम से चल रहा हूँ। –

+0

@anupamsr जब भी आप 'ट्री :: नोड :: get_child()' कहते हैं तो आप स्टैक पर 'std :: map' बनाते हैं:' मानचित्र बच्चे; ' – bames53

+0

@ bames53: लेकिन आवंटन ढेर पर रिपोर्ट किया जाता है। यह मेरा भ्रम है। कार्यक्रम में धीमी गति से बड़ी संख्या में मुझे महसूस किया जा सकता है। उस रेखा को हटाने के बाद भी मुझे आवंटित आवंटन की एक ही राशि मिलती है। –