New未调用,但已分配内存
new not called, yet memory allocated
我写了一个简单的Trie实现。下面是源代码:
#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(' ')) {
}
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;
}
下面是我的测试程序:
#include <iostream>
#include <sstream>
#include "Trie.h"
int main() {
Trie t;
for (unsigned int i = 0; i < 10000; ++i) {
t.insert("hello");
}
return 0;
}
我的问题是,即使'hello'在第二次尝试插入时已经插入,并且因此不再调用new
,大量内存正在分配和取消分配。这个数量随着I增加max I的值而增加。例如,在上面的例子中,valgrind给出了这样的输出:
==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
我已经确认调用Node()构造函数的次数是恒定的。那么为什么以及如何分配和释放所有内存呢?
每次调用insert
时,都会传递给它一个const char[6]
,但它期望的是const std::string&
,因此每次迭代都会创建一个临时的std::string
,然后将其传递给函数,然后在下一次迭代之前销毁。这样就澄清了10000个分配和释放,只剩下11个,这可能是您对节点的分配,以及std::map
在内部所做的事情,以及我忽略的其他一些地方(例如字符串或映射的副本)
容器可以分配内存,即使它不包含任何元素,但我认为它应该被设计成其他方式,并且如果任何主要的容器实现都做了这样的事情,我会感到惊讶。(deque 可能是个例外)
std::map
将动态分配自己的内存,并且每次调用get_child()
时都创建一个新的内存。当使用默认构造函数时,它分配了多少内存我不能说,但它可能是一些。仅仅因为你没有调用new
并不意味着你的类创建的其他类型不会调用。
同样,std::map
不会为每个插入的元素分配一个全新的堆存储空间。这将是非常低效的。它有一些内部算法来在需要时增加它的后备存储,并且它肯定会分配比需要的更多的空间来容纳一个新元素。
相关文章:
- 在c++中为我自己的基于指针的数组分配内存的正确方法
- 给定一个指向堆分配内存的指针,智能指针实现如何为其找到合适的释放函数?
- 如果 const 不分配内存,为什么我可以获取 const 的地址?
- 在函数中分配内存时出现问题
- 如何为 std::vector 分配内存,然后稍后为某些元素调用构造函数?
- constexpr new 如何分配内存?
- 在构造函数中分配内存失败是如何冒泡的
- LLVM 传递以在特定地址分配内存
- CudaMalloc 在分配内存时失败
- 为什么它在不分配内存的情况下工作正常
- 为什么在正确解除分配内存时出现内存泄漏?
- 如何通过 malloc 为队列数组分配内存?
- vector是否为std::移动的对象连续分配内存
- 删除类成员的动态分配内存的最佳方法是什么
- 唯一指针是否在堆或堆栈上分配内存?
- 如果不分配内存,我如何能够为变量创建和分配值?
- std::initializer_list 堆是否分配内存?
- 如何按顺序或在指定的地址分配内存?
- 是否可以使用 malloc 为类对象分配内存?
- 迭代器是否分配内存(如指针)?