我的代码中是否存在内存泄漏?

Do I have a memory leak in my code?

我在 IntList 实现 push_back 节点中有一小段代码。我的问题是 *pnode 是否会导致内存泄漏,或者我是否需要在最后删除它。

void IntList::push_back(int data){
    if (first){
        IntNode *pNode = first;
        while(pNode->next!=0){pNode = pNode->next;}
        pNode->next = new IntNode(data);
    } else
        first = new IntNode(data);
}

不,您不需要在 pNode 上致电 delete。您只能对使用 new 创建的事物调用 delete。使用现在的代码 pNode 是一个堆栈对象,当它在函数结束时超出范围时将自动销毁。

您最终必须删除使用 new 创建的 IntNode,可能在容器的析构函数和 pop_back 函数中。 pNode本身(指针)是在栈上分配的,并没有new,所以不需要删除。

您不需要删除 pNode。此外,在这种特殊情况下你不能这样做。 使用 new 创建内容后,您必须 delete 只使用一次 - 一旦您永远不会使用它。 使用 delete 删除对象后,尝试读取其内容是一个 undefined behavior. The pointers are generally one of the most bug generating part of the ,因此您尝试更好地理解它是件好事。

你可以这样想象自己:你可以用new买房子(房子是一段记忆)。它returns 你的地址好象呢。它现在是您的房子,您可以随心所欲地使用它。您也可以用 delete 出售它。在此之前,您可以将您的家庭住址告诉您的朋友,这样他们就可以来找您。地址的分配,导致复制它(例如 IntNode *pNode = first;)。但是,您仍然只有一所房子。所以无论你复制多少次你的家庭地址,你只能卖一次房子。

我建议使用智能指针(例如std::unique_ptr),但我认为这个程序是为了学习编程,所以不要这样做;)

您需要在从列表中删除节点的每个函数上删除节点,而不是在插入时。不这样做会导致泄漏。

如果在销毁对象时仍有分配的节点,则需要遍历整个列表以删除所有节点。不这样做也会导致泄漏。

我想这是一项大学作业,因为那里有数百万经过实战检验的链表实现。

如果您在所有函数上都保持一个尾节点是最新的,您会做得更好,这样您就可以避免迭代整个列表以在尾部插入一个节点。

您显示的代码不足以判断您是否正在泄漏内存。您显示了负责分配的例程,但没有显示您执行释放的代码。

如果您没有任何执行重新分配的代码,那么是的,显然是泄漏。 C++ 不执行自动垃圾收集,就像您在其他一些语言中可能习惯的那样。

您正在使用裸 new,因此即使您确实有一些其他代码试图进行重新分配,也有一个很好的改变,它被错误地完成了。


在 C++ 中,您通常不应直接使用 new 运算符,而应该学习使用 RAII 来处理资源。 IntList 大概使用拥有原始指针,这是另一件要避免的事情。在这种情况下,您可能应该使用 unique_ptr<IntNode>。例如:

struct IntList {
  struct IntNode {
    unique_ptr<IntNode> next;
    int data;

    IntNode(int data) : data(data) {}
  };

  unique_ptr<IntNode> first;

  // returns a reference to the null pointer which terminates the linked list
  unique_ptr<IntNode> &get_tail() {
    if (!first) {
      return first;
    }
    IntNode *pNode = first.get(); // pNode is a non-owning pointer
    while (pNode->next) {
      pNode = pNode->next.get();
    }
    return pNode->next;
  }

  void push_back(int data) {
    get_tail() = make_unique<IntNode>(data);
  }
};

上面的代码确实有问题,但与内存泄漏无关。当列表销毁时,first自动销毁,first->next自动销毁,依此类推。有可能将如此多的元素插入到列表中,以至于此破坏链 'smashes' 函数堆栈,导致未定义的行为。

可以使用以相反顺序销毁节点的 IntList 析构函数来解决此问题:

IntList::~IntList() {
  while (first) {
    unique_ptr<IntNode> tmp = std::move(first);
    first = std::move(tmp->next);
  }
}

有趣的是,这是 Rule of Three 的一个例外示例。