【问题标题】:Deallocate pointer to a struct preserving memory addresses of nested variables释放指向保留嵌套变量内存地址的结构的指针
【发布时间】:2017-07-28 12:53:02
【问题描述】:
template<typename T>
T& List<T>::popFront()
{
    if (head == NULL)
        throw std::underflow_error("List error: no head node available\n");
    if (tail == head)
        tail = NULL;
    T item = head->item;
    head = head->next;
    return item;
}

我有一个包含以下字段的递归结构 ListNode:

template <typename T>
struct ListNode {
    T item;
    ListNode* next;
    ...
}

问题是我想在 popFront 过程之后释放一个头节点,但是由于所有嵌套节点都间接指向同一个地址,它们的地址也从堆中消失了。所以现在正如你在上面看到的,我只是将头节点的指针地址更改为下一个,我认为这会导致内存泄漏。

我不排除我对这种方法和我的假设绝对错误。请考虑执行此类任务的最有效方式,如果这种释放确实是必要的。

【问题讨论】:

  • 您返回对本地自动变量的引用。使用返回的引用具有未定义的行为。
  • @user2079303 是的,没注意到,修复它。
  • " 请考虑执行此类任务的最有效方式" 答案是使用久经考验的标准库,所以std::liststd::forward_list .它的作者知道他们在做什么并编写高效的代码。
  • @underscore_d 显然在实际项目中我将使用标准库中的功能,但为了寻求经验,我尝试自己实现一些数据结构
  • @KirillKorolev 当自己实现数据结构只是为了好玩时,我建议寻找最简单的正确方法,而不是首先寻找最有效的方法。如果你幸运的话,它们是一样的。

标签: c++ pointers memory-leaks nested


【解决方案1】:

这里有几个问题。

template<typename T>
T& List<T>::popFront()
{
    if (head == NULL)
        throw std::underflow_error("List error: no head node available\n");
    if (tail == head)
        tail = NULL;
    T item = head->item;
    head = head->next; //Memory leak, you just lost your only pointer to the head item
    return item; //Returning reference to stack variable, undefined behavior
}

我建议您将签名更改为按值返回,以便您可以返回本地并释放堆中的元素。

template<typename T>
T List<T>::popFront()
{
    if (head == NULL)
        throw std::underflow_error("List error: no head node available\n");
    if (tail == head)
        tail = NULL;
    T item = head->item;
    ListNode* old_head = head; //keep this for deallocation
    head = head->next;
    delete old_head; //Deallocate the old head
    return item; //Return by value
}

当然,您可以采用std::list 的行为,并有不同的访问和弹出方法,分别为front()pop_front()

根据签名,front() 返回一个引用,如果T 是一个重对象,则效率更高。

当然,这一切都假设您是出于学术目的。否则,好吧,使用std::list 或类似的标准库容器。

【讨论】:

  • 感谢您的帮助,但是正如我在帖子中所说,我使用了这种方法,但是删除“old_head”后的head是:0x00cce9c0 {item=-572662307 next=0xdddddddd {item=???下一个=??? } } 引发异常。是的,只是为了体验。
  • head.next 看起来像垃圾值。您确定在构造时或在插入列表的某个点将 next 初始化为 NULL 吗?你确定这个垃圾值不会在head = head-&gt;next; 之后立即发生吗?出于某种原因,head 是否指向自身意味着删除old_head 实际上会删除新的head
  • 正如 patatahooligan 所说,在调用 popFront() 时检查 head-&gt;next 指向的内容。您的问题可能不在 popFront() 函数中,而是在其他地方,这使您的结构不好。
  • @Ramon 结构体的解构器似乎是个问题。我刚刚意识到,当我释放一个节点时,我不想释放下一个指针。所以我稍微改变了一个解构器,一切似乎都正常工作。感谢您的宝贵时间。
  • @KirillKorolev 是的,没错,ListNode 结构不应该释放next,因为你在List 类中管理它。我的荣幸,干杯!
【解决方案2】:

当你弹出头部时,在删除它之前,抓住下一个指针并使其成为头部。

template<typename T>
T List<T>::popFront()
{
    if (head == NULL)
        throw std::underflow_error("List error: no head node available");
    if (tail == head)
        tail = NULL;
    T item = head->item;
    ListNode *next = head->next;    // Grab next
    delete head;                    // Now it is safe to delete head
    head = next;                    // Head now points to next
    return item;
}

这里需要尾巴吗?当只有一个节点时,为什么不让下一个头等于NULL

编辑:刚刚注意到。您不应该返回对Item 的引用,因为我们正在销毁持有它的ListNode。按值返回。或者,您可以返回 ListNode* 而不是删除它,但这可能不是您希望 List 的界面工作的方式。

【讨论】:

  • 其实我已经测试过这段代码,不幸的是,它没有工作。我之前尝试过这个,但正如我在删除“下一个”指向已释放内存后所说的那样。而tail用于在恒定时间内执行pushBack之类的操作。
  • delete head然后在head = head-&gt;next中使用,此时head是一个无效指针。
  • 已更正。它应该使用本地下一个指针。感谢您指出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多