简体   繁体   中英

How to prevent memory leak in generalized linked list?

I've implemented my own linked list data structure. Data is stored inside Node struct. Code is as follows

// NODE

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

template <typename T>
Node<T>::Node(T d) : data(d), next(NULL) {}
// LIST

#include "node.cpp"

template <typename T>
class List
{
    Node<T> *head;
    int size;

public:
    List();                       // Default constructor
    List(const List &);           // Copy constructor
    void push_back(const T &);    // Insert element to the end of the list
    int get_size() const;         // Get the current size of the list
    T &operator[](int) const;     // Overload [] operator
    void operator=(const List &); // Overload = operator
    ~List();                      // Destructor
};

template <typename T>
List<T>::List() : head(NULL), size(0) {}

template <typename T>
List<T>::List(const List &list) : head(NULL), size(0)
{
    for (int i = 0; i < list.size; i++)
        push_back(list[i]);
}

template <typename T>
void List<T>::push_back(const T &data)
{
    // Create new Node with data
    Node<T> *nn = new Node<T>(data);

    // Find insert position
    if (head == NULL)
    {
        head = nn;
        size++;
        return;
    }

    Node<T> *traverse = head;
    while (traverse->next)
        traverse = traverse->next;

    // Traverse points to end of the list
    traverse->next = nn;
    size++;
}

template <typename T>
int List<T>::get_size() const
{
    return size;
}

template <typename T>
T &List<T>::operator[](int index) const
{
    int count = 0;
    Node<T> *traverse = head;
    while (traverse && count < index)
    {
        traverse = traverse->next;
        count++;
    }
    return traverse->data;
}

template <typename T>
void List<T>::operator=(const List<T> &list)
{
    Node<T> *traverse = head;

    while (head)
    {
        traverse = head;
        head = head->next;
        delete traverse;
    }

    size = 0;
    for (int i = 0; i < list.getSize(); i++)
        push_back(list[i]);
}

template <typename T>
List<T>::~List()
{
    Node<T> *traverse = head;

    while (head)
    {
        traverse = head;
        head = head->next;
        delete traverse;
    }
}

Problem is with memory leak. Consider the following main file

#include "list.cpp"

using namespace std;

List<int *> l;

void func()
{
    int *i = new int[2];
    i[0] = 1;
    i[1] = 2;
    l.push_back(i);
}

int main()
{
    func();
    return 0;
}

This program has memory leak according to Valgrind. It is because that Node does not have a destructor so it can not delete data inside it. However, I can not add a destructor to Node because, suppose that I am using List<int> so it is an error to delete something that was not dynamically allocated. In short, whenever I use a dynamically allocated data type for List , I get memory leak. How can I overcome this situation? Thanks.

The leak in your example has nothing to with the list. You leak the same with:

void func()
{
    int *i = new int[2];
    i[0] = 1;
    i[1] = 2;
}

You have to delete what you created via new and delete[] what you created via new[] . To fix the leak:

void func()
{
    int *i = new int[2];
    i[0] = 1;
    i[1] = 2;
    l.push_back(i);
    delete [] i;
}

However, note that then after the delete[] you have a dangling pointer in the list.

It is not the List s buisness to delete objects when you push raw pointers to it. The list cannot know if those are owning pointers or not. For example:

void func()
{
    int i = 0;
    l.push_back(&i);
}

No need to delete anything here. (Though, same here: once the function returns you have a dangling pointer in the list)

Neither of the abvove is really "ok". Don't use raw owning pointers. Use smart pointers instead. And if you want a list of integers then use a List<int> (or rather a std::list<int> ).

Use std::unique_ptr as the data type for the nodes, eg:

List<std::unique_ptr<int[]>> l;

When each node is destroyed, its destructor will destroy its unique_ptr data, which will in turn call delete[] on the int* pointer it is holding.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM