【问题标题】:trouble deleting a dynamic linked list?删除动态链表时遇到问题?
【发布时间】:2013-03-19 02:04:25
【问题描述】:
  struct Package_Node
    {
        int bar_code;
        float package_weight;
        Package_Node *next_packaged;
    };

    struct Key_Node
    {
        int key;
        Package_Node *next_package;
    };        

   for(int i=0; i<3; i++)
            {
                if(keyMain[i].next_package==NULL)
                {
                    continue;
                }

                if(keyMain[i].next_package!=NULL)
                {
                    nPointer3=keyMain[i].next_package;
                    nPointer4=keyMain[i].next_package;
                    while(nPointer3)
                    {
                        nPointer4=nPointer4->next_packaged;
                        delete[] nPointer3;
                        nPointer3=nPointer4;
                    }
                }
            }

keyMain 由给定结构key_node 描述的数组。

Key main 本身是一个动态数组,但为了代码的缘故,我将其显示为一个静态数组。

假设数组有 3 个索引,0,1,2,每个索引包含一个单独的链表。现在我正在尝试删除每个链接列表,但似乎有些正在被删除,而有些则没有。

如何解决这个问题?

【问题讨论】:

  • 你能显示nPointer3指向的内存被分配的行吗?
  • 你为什么使用delete [] 作为Package_Node 元素?如果它们是数组,那么数组的其余部分呢?
  • @ChrisDodd Package_Node 不是数组。它是一个链表。 Key_node 然而是一个数组。
  • @Digikata 我不确定你的问题是什么,但如果我理解正确,nPointer3 是一个指向package_node 的指针。我使用package_node *nPointer3,然后使用nPackage = new Package_node创建分配的动态内存
  • 如果您使用 new Package_Node 一次分配一个节点,您将获得使用数组删除运算符 delete [] 释放内存的未定义行为.从单数 new 运算符分配的内存应使用单数 delete 释放。同样,数组 delete 只能与数组 new 运算符 new Type[n] 配对。

标签: c++ dynamic-arrays


【解决方案1】:

你在这里做错了什么?一方面,您正在手动管理内存。

#include <memory>

struct Package_Node
{
  int bar_code;
  float package_weight;
  std::unique_ptr<Package_Node> next_packaged;
};

struct Key_Node
{
  int key;
  std::unique_ptr<Package_Node> next_package;
};

要将指针存储在unique_ptr 中,请使用reset(new Package_Node())。而不是delete,请致电reset()。当unique_ptr 被删除时,unique_ptr 指向的所有内容都会被自动删除。

这是 C++11 -- 在 C++03 中,您可能有 std::tr1::unique_ptrboost::unique_ptr,但没有移动语义 unique_ptr 使用起来更危险。


现在,如果您执行上述操作,您的代码会变得更短,并且释放整个链表只需 .reset() 指向第一个节点的智能指针:其他所有内容都会自动删除!

但是,您可能不愿意这样做。所以我会攻击你代码中的一些其他问题。

您的变量名称不反映值的使用。 nPointer4 是一个可怕的变量名。试试PointerToDelete,它记录了它的目的是什么。此外,您应该在初始化时声明指针,并在它无效时将其清除,而不是让它在使用之前或之后挂起。 (重用变量不会获得奖励积分)。

看看这个循环:

            nPointer3=keyMain[i].next_package;
            nPointer4=keyMain[i].next_package;
            while(nPointer3)
            {
                nPointer4=nPointer4->next_packaged;
                delete[] nPointer3;
                nPointer3=nPointer4;
            }

注意nPointer3nPointer4 应该在while 的开头保存相同的数据,那么为什么它们都存在呢?你可以通过这样重写将你的状态减半:

            Package_Node* ptrToDelete=keyMain[i].next_package;
            while(ptrToDelete)
            {
                Package_Node* nextPtr = ptrToDelete->next_packaged;
                delete ptrToDelete;
                ptrToDelete = nextPtr;
            }

注意ptrToDelete 仅在初始化时存在。

注意nextPtr 的存在时间尽可能短,然后就超出了范围。它也只在初始化后才存在。

接下来,始终发布实际演示您的问题的编译代码。您上面的代码由于与您的问题完全无关的原因无法编译,并且阅读您的想法不是您应该要求人们做的事情。

你不知道什么是错的,那么你怎么知道什么是重要的或不重要的?简单:让你的编译器弄清楚。制作一个简单的示例来演示您遇到的问题,编译它,测试问题是否仍然存在,然后发布那个

与其遇到问题,不如删除部分以使其更短,发布可能或可能没有问题的非编译代码,并希望其他人能读懂你的想法。

您对struct Package_Node* 的使用完全没有必要——Package_Node* 更短,而且(除了一些极端的极端情况)做同样的事情。

即使您不使用智能指针,拥有指针的struct 也应该在其析构函数中销毁它,如果它不拥有该指针,则应该使用其他东西。 RAII 是你的朋友。当你创建一个拥有指针的struct 时,不要忘记关注rule of three

【讨论】:

  • 我这里没有手动管理内存。我只是为了代码而使用了手动的东西。
  • 整个程序大约有 700 行长,我使用给定的structs 完成了整个程序。有没有其他方法可以解决这个问题?另外我真的必须坚持给定的structs,因为这是一个学校项目。
  • @QaziHossain 这没有任何意义。 “我不是在这里手动管理内存”和“我只是使用手动的东西”——你要么是手动管理内存,要么不是。
  • 代码大约有 700 行长,我会发布整个代码,但我认为没有人会读那么长的代码。除了要求编译器删除链表的所有成员的部分之外,该代码可以正常工作。它正在删除一些成员,但其余成员仍然存在。
  • @QaziHossain 不要发布整个代码。发布一个独立的、完整的、简短的示例,可以实际编译并演示您的问题。
【解决方案2】:

由于您在 cmets 中提到 nPointer3 内存是通过“new Package_Node”分配的,因此行“delete[] nPointer3;”应改为:

delete nPointer3;
nPointer3 = NULL;  // recommended (mentioned in another answer)

您描述的行为可能是由于尝试释放通过单数 new 分配的 nPointer3 内存时的未定义行为。只有通过数组 new 运算符“new[]”(例如 new Package_Node[16])分配的内存应该用“delete []”释放。

【讨论】:

    【解决方案3】:

    我不知道你是怎么检查东西是否被删除的;一旦您释放了内存,就可以重新使用该内存并且您不能再访问它 - 尽管除非您导致运行时错误,否则没有人会阻止您 - 但释放不会修改该内存的内容。
    您无法通过检查来判断某个特定对象已被释放。

    我相信您的问题是您完成后忘记将keyMain[i].next_package 设置为NULL

    for(int i = 0; i < 3; i++)
    {
        if (keyMain[i].next_package != NULL)
        {
            Package_Node* current = keyMain[i].next_package;
            Package_Node* next = 0;
            while (current)
            {
                next = current->next_package;
                // No "[]" since you're apparently not allocating with "[]"
                delete current;
                current = next;
            }
            keyMain[i].next_package = NULL;
        }
    }
    

    【讨论】:

    • 您将删除两次keyMain[i].next_package -- 一次是在循环的第一次迭代中使用delete[],第二次是在循环之后使用delete
    • 所以你说一切都是对的,除了我需要将key_node 中的每个指针设置为空?
    • @ChrisDodd 感谢您发现这一点。
    • @QaziHossain 是的,据我从您的代码中可以看出,但我建议您也检查一下您的变量范围和命名原则。具有非描述性名称或不必要的大范围的变量会使代码更难理解,这往往会导致错误。
    • @QaziHossain 正如我所说,您无法检查内存是否已被释放。您所能做的就是完成循环并说服自己每个元素都被删除了。换句话说,使用逻辑。当您没有将其设置为NULL 时,您只是在打印这些对象被释放时留下的东西——实际上已经没有列表了。有时,该内存被重用并设置为 0,完全是偶然的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多