【问题标题】:removing pointers from linked list in c++从 C++ 中的链表中删除指针
【发布时间】:2012-08-10 07:00:31
【问题描述】:

我写了这个哈希图(这是电话面试练习的一部分),当我放一个元素时,我做了一个new Node(key, value)。我想确保在哈希图本身超出范围时进行清理。

我在这里错过了什么吗?有什么方法可以检查是否存在内存泄漏?

class HashMap {
private:
    list<Node*> data[SIZE];

public:
    ~HashMap();
    Node* get(int key);
    void put(int key, int value);

    int hashFn(int val){ return val % 13; }
};

HashMap::~HashMap(){
    for(int i = 0; i < SIZE; ++i){
        list<Node*>& val = data[i];
        for(list<Node*>::iterator it = val.begin(); it != val.end(); it++){
            Node* n = *it;
            delete n;
        }
    }
}

对于古玩:完整代码在这里:http://rextester.com/EHPCYW12862

编辑:

另外,我真的需要最后调用 list.clear() 吗(因为我已经释放了列表中的所有节点)?

【问题讨论】:

  • 看起来不错,但使用智能指针会更好
  • 使用boost::ptr_list,问题就解决了。
  • 你为什么不使用列表向量呢?
  • 这是一个面试问题,所以不能使用externals :)
  • @AndersK :我想要一个固定大小的数组,而不是会增长的东西。 (查看我提供的链接,了解我是如何实现它的)。只有SIZE -1 的桶数,因此我不需要数组增长。

标签: c++ memory memory-leaks hashmap


【解决方案1】:

似乎put 正在构造一个Node 以放入您的哈希表,关联keyvalue。没有必要使用list&lt;Node *&gt;,而是使用list&lt;Node&gt; 会更简洁。

list<Node> data[SIZE];
//...
data[bucket].push_front(Node(key, value));

那么,您可以避免实现析构函数。

您的get 函数仍然可以返回一个指针。

Node* HashMap::get(int key){
    //...
    list<Node>::iterator it = data[bucket].begin();
    //...
            if (it->key == key) return &*it;
    //...
    return NULL;
}

如果您将实现保留为list&lt;Node *&gt;,那么您还应该实现一个复制构造函数和一个赋值运算符 (the rule of three)。

【讨论】:

  • 我最初使用的是 Node.js。但是,当我再次尝试put() 相同的值时,我被要求覆盖该节点的值。我将如何仅使用节点来做到这一点?
  • 你的get可以返回一个引用,允许你修改返回的Node。但更清洁的可能是返回迭代器的私有get_iter,以便getput 都可以使用它。
  • 正是我的想法:)。但是,当找不到节点时,我无法通过引用返回 NULL。所以这样的事情是行不通的:Node&amp; get(int key)
  • @brainydexter:面试官是否定义了界面?如果没有,您可以返回 bool,并接受对 Node &amp; 的引用以进行填充。或者,用&amp;*iter返回找到的Node的地址。
  • @KirillKobelev:错字!谢谢!
【解决方案2】:

检查是否没有内存泄漏的最好方法是使用不会泄漏的智能指针类。 shared_ptr&lt;Node&gt;unique_ptr&lt;Node&gt; 可以在这里做,第一个用于可复制的地图,第二个用于不可复制的地图。

但是,如果您必须使用原始指针(作业?),则缺少一些东西:复制构造函数和赋值运算符。如果没有禁用或实现它们,复制此 HashMap 将产生悬空指针(在其中一个映射被销毁后)。

【讨论】:

  • 同意。此处应遵循三原则。我被要求实现一个专注于 get()/put() 函数的哈希映射
【解决方案3】:

清理工作很好。小点:

   for(list<Node*>::iterator it = val.begin(); it != val.end(); ++it)

出于性能原因,最好使用前缀增量。后缀形式必须在增量之前给出迭代器的状态。该对象将立即被丢弃。编译器可能会优化,但这取决于。

【讨论】:

  • 啊,是的,我不久前就知道了。感谢您指出了这一点。 (我确实在它上面的循环中使用了++i :)
  • 对于 int 值,这无关紧要。对于作为对象的迭代器,这一点更为重要。
  • list&lt;&gt; 的检测器无论如何都会清理它。我建议不要打电话。
【解决方案4】:

看了你的代码,发现你使用了一些开销结构。

这两个sn-ps是等价的

Node ** d = &(*it); 
if((*d)->key == key){
    return *d;
}

if((*it)->key == key){
    return (*it);
}

【讨论】:

  • 啊,是的!面试的时候,我很慌张,不想传一份给指针。我后来意识到了这一点:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-06
  • 2013-12-29
  • 2013-07-02
  • 1970-01-01
  • 2017-04-05
相关资源
最近更新 更多