【问题标题】:Destructor for LinkedList of LinkedLists链表的链表的析构函数
【发布时间】:2016-01-31 12:01:10
【问题描述】:

对于我的一个编程课程的作业,我们必须创建一个邻接表,它是一个看起来像这样的链表的链表。

A->B->C

B->A->D

C->D

D->A->B->C

我在尝试释放析构函数中分配的内存时遇到内存泄漏问题。我已经尝试了一段时间,但还没有找到/想出任何可行的解决方案。

另外,请忽略头文件中包含的实现。我们被告知可以完成这项任务。

Valgrind 错误消息:

==2316== 堆摘要:
==2316== 在退出时使用:2 个块中的 48 个字节
==2316== 总堆使用量:3 个分配,1 个释放,64 个字节分配
==2316==
==2316== 1 个块中的 48 个(32 个直接,16 个间接)字节在 2 个丢失记录 2 中肯定丢失
==2316== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2316== by 0x4012EE: main (main.cpp:34)
==2316==
==2316== 泄漏摘要:
==2316== 肯定丢失:1 个块中的 32 个字节
==2316== 间接丢失:1 个块中的 16 个字节
==2316== 可能丢失:0 个块中的 0 个字节
==2316== 仍然可以访问:0 个块中的 0 个字节
==2316== 抑制:0 个块中的 0 个字节
==2316==
==2316== 对于检测到和抑制的错误计数,重新运行:-v
==2316== 错误摘要:来自 1 个上下文的 1 个错误(已抑制:来自 0 的 0 个)

这是我正在使用的一些代码(使用 gcc c++11 编译):
链接列表.h

#ifndef LINKEDLIST_H
#define LINKEDLIST_H

template<typename T> struct Node
{
    T data;
    Node<T>* next;
};

template<typename T> class LinkedList
{
private:
    Node<T>* head;
    Node<T>* tail;
    Node<T>* curr;
    unsigned int size;

    void insertAtHead(T val)
    {
        Node<T>* temp = new Node<T>;
        temp->data = val;
        temp->next = nullptr;
        head = temp;
        tail = temp;
        curr = temp;
        size++;
    }

public:
    LinkedList()
    {
        head = nullptr;
        tail = nullptr;
        curr = nullptr;
        size = 0;
    }

    ~LinkedList()
    {
        Node<T>* nodePtr = head;
        Node<T>* temp;

        while (nodePtr != nullptr)
        {
            temp = nodePtr;
            nodePtr = nodePtr->next;
            delete temp;
        }

        size = 0;
    }

    void insertAtTail(T val)
    {
        if (head == nullptr)
            insertAtHead(val);
        else
        {
            Node<T>* temp = new Node<T>;
            temp->data = val;
            curr->next = temp;
            temp->next = nullptr;
            tail = temp;
            curr = temp;
            size++;
        }
    }
    // returns the value at the node location passed if it exists within the
    // linked list, otherwise nothing is returned
    T get(int location)
    {
        // std::cout << "size: " << size << std::endl;

        if (location >= 0 && location <= size)
        {
            Node<T>* temp = head;
            unsigned int counter = 0;

            while (counter != location)
            {
                temp = temp->next;
                counter++;
            }

            return temp->data;
        }
    }
};
#endif // LINKEDLIST_H

main.cpp

#include "linkedlist.h"

int main()
{
    LinkedList<LinkedList<int>*> matrix;
    matrix.insertAtTail(new LinkedList<int>);
    matrix.get(0)->insertAtTail(6);

    return 0;
}

【问题讨论】:

  • 并非get(location) 的所有路径都返回值。使用编译器的警告来查找此类问题(例如-Wall -Wextra -pedantic
  • 你在堆上声明了一个 LinkedList&lt;int&gt; 并且永远不会删除它。我错过了什么?

标签: c++ c++11 memory-management memory-leaks destructor


【解决方案1】:

并非get(location) 的所有路径都返回值。

使用编译器的警告来查找此类问题(例如-Wall -Wextra -pedantic)。

另外,请确保在构造时初始化所有成员。

struct Node
{
    T data {};
    Node<T>* next = nullptr;
};

Node<T>* head = nullptr;
Node<T>* tail = nullptr;
Node<T>* curr = nullptr;

更新

在仔细查看之后,确实您永远不会删除外部列表中数据T 的指针。由于您的列表不知道 T 是否是(拥有的)指针,因此无法决定删除。

通常,我们使用智能指针包装器来解决这个问题。在您的情况下,您可能不会“被允许”使用它,因此,请在删除列表节点之前编写额外的循环来删除数据指针。

建议修复

这是一个重新设计的示例,它可以选择采用 NodeFree 类型的模板参数,因此您可以为外部列表传入 std::default_delete&lt;T&gt;

template <typename T> struct DefaultNodeFree {
    void operator()(T &) const {}
};

template<typename T, typename NodeFree = DefaultNodeFree<T> > class LinkedList
{
private:

    struct Node {
        T data {};
        Node* next = nullptr;

        ~Node() { NodeFree()(data); }
    };

所以你可以像这样使用它:

typedef LinkedList<int> inner;
LinkedList<inner*, std::default_delete<inner> > matrix;

matrix.insertAtTail(new LinkedList<int>);
matrix.get(0)->insertAtTail(6);

泄漏消失了。

现场演示

Live On Coliru

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <cassert>
#include <memory>

template <typename T> struct DefaultNodeFree {
    void operator()(T &) const {}
};

template<typename T, typename NodeFree = DefaultNodeFree<T> > class LinkedList
{
private:

    struct Node {
        T data {};
        Node* next = nullptr;

        ~Node() { NodeFree()(data); }
    };

    Node* head = nullptr;
    Node* tail = nullptr;
    Node* curr = nullptr;
    unsigned int size;

    void insertAtHead(T val)
    {
        Node* temp = new Node;

        temp->data = val;
        temp->next = nullptr;

        head = temp;
        tail = temp;
        curr = temp;

        size++;
    }

public:
    LinkedList()
    {
        head = nullptr;
        tail = nullptr;
        curr = nullptr;
        size = 0;
    }

    ~LinkedList()
    {
        Node* nodePtr = head;

        while (nodePtr != nullptr)
        {
            Node* temp = nodePtr;
            nodePtr = nodePtr->next;
            delete temp;
            size -= 1;
        }

        assert(size == 0);
    }

    void insertAtTail(T val)
    {
        if (head == nullptr)
            insertAtHead(val);
        else
        {
            Node* temp = new Node;
            temp->data = val;
            curr->next = temp;
            temp->next = nullptr;
            tail = temp;
            curr = temp;
            size++;
        }
    }

    // returns the value at the node location passed if it exists within the
    // linked list, otherwise nothing is returned
    T get(unsigned location)
    {
        // std::cout << "size: " << size << std::endl;

        if (location >= 0 && location <= size)
        {
            Node* temp = head;
            unsigned int counter = 0;

            while (counter != location)
            {
                temp = temp->next;
                counter++;
            }

            return temp->data;
        }

        return {};
    }
};
#endif // LINKEDLIST_H

int main()
{
    typedef LinkedList<int> inner;
    LinkedList<inner*, std::default_delete<inner> > matrix;

    matrix.insertAtTail(new LinkedList<int>);
    matrix.get(0)->insertAtTail(6);
}

【讨论】:

  • 为此添加了一个典型的通用 C++“修复”(当然,除非一个人通常不会一开始就写一个链接 :))Live On Coliru
【解决方案2】:

好吧,根据我的经验,当您使用模板时,您可以在同一个头文件中实现它们,但您必须将实现与声明分开。

一个常见的解决方案是在头文件中编写模板声明,并在头文件的末尾包含实现。 在此处阅读最高投票Why can templates only be implemented in the header file?

我不确定这是否是您的问题,但上次我尝试按照您的方式实施模板时,我的程序根本无法运行。

【讨论】:

猜你喜欢
  • 2013-03-18
  • 2013-04-26
  • 2012-10-15
  • 2018-03-27
  • 2014-05-27
  • 2017-06-12
  • 2016-12-08
  • 2020-07-19
相关资源
最近更新 更多