链表push_front和push_back中的C ++内存泄漏

时间:2018-12-19 19:24:03

标签: c++ memory-leaks linked-list valgrind doubly-linked-list

我通过valgrind收到此内存泄漏错误:

item_done

我的 24 bytes in 1 blocks are definitely lost in loss record 1 of 11 at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334) by 0x413E47: pjc::list::push_back(double) (list.cpp:33) by 0x416371: ____C_A_T_C_H____T_E_S_T____4() (tests-list-01.cpp:86) 24 bytes in 1 blocks are definitely lost in loss record 2 of 11 at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334) by 0x414047: pjc::list::push_front(double) (list.cpp:66) by 0x4192C1: ____C_A_T_C_H____T_E_S_T____10() (tests-list-01.cpp:146) 链表文件如下所示:

.hpp

using std::size_t; namespace pjc { class list { private: struct node { double val = 0; node* prev = nullptr; node* next = nullptr; }; node* head = nullptr; node* tail = nullptr; size_t num_elements = 0; public: list() = default; list(const list& rhs); list& operator=(const list& rhs); list(list&& rhs); list& operator=(list&& rhs); ~list(); void push_back(double elem); void push_front(double elem); }; push_back()和链表的析构函数的定义如下:

push_front()

我试图更改析构函数,但这似乎还可以。我真的没有任何想法,无论泄漏发生在哪里。

编辑:对不起,我第一次省略了代码的一些重要部分。现在,它应该遵循5的规则。

2 个答案:

答案 0 :(得分:1)

在您显示的代码中我什么都看不到,但是您没有显示所有相关代码。

例如,如何使用list对象可能是导致泄漏的原因。例如,如果您没有通过实现适当的复制和移动构造函数以及复制和移动赋值运算符来遵循Rule of 3/5/0,则在复制/移动list对象时可能会泄漏内存。但是您没有显示该代码,因此我们无法确定您的操作是否正确。

话虽如此,您的析构函数确实有一个额外的delete不属于您,并且您的push_back()push_front()方法可以简化。

最安全的选择是简单地使用std::list并让它为您管理内存。但是,如果您想手动进行操作,请尝试以下操作:

class list
{
private:
    struct node
    {
        double val;
        node* prev = nullptr;
        node* next = nullptr;

        node(double value = 0) : val(value) {}
    };

    node* head = nullptr;
    node* tail = nullptr;
    size_t num_elements = 0;

public:
    list() = default;
    list(const list &src);
    list(list &&src);
    ~list();

    list& operator=(const list &rhs);
    list& operator=(list &&rhs);

    void push_back(double elem);
    void push_front(double elem);

    void swap(list &other)
};

list::list(const list &src)
    : list()
{
    for(node *n = src.head; n != nullptr; n = n->next)
        push_back(n->val);
}

list::list(list &&src)
    : list()
{
    src.swap(*this);
}

list::~list()
{
    node *n = head;
    while (n)
    {
        node *next = n->next;
        delete n;
        n = next;
    }
}

list& list::operator=(const list &rhs)
{
    if (this != &rhs)
        list(rhs).swap(*this);
    return *this;
}

list& operator=(list &&rhs)
{
    list(std::move(rhs)).swap(*this);
    return *this;
}

void list::push_back(double elem)
{
    node *n = new node(elem);
    if (tail)
    {
        tail->next = n;
        n->prev = tail;
    }
    else
        head = n;
    tail = n;
    ++num_elements;
}

void list::push_front(double elem)
{
    node *n = new node(elem);
    if (head)
    {
        head->prev = n;
        n->next = head;
    }
    else
        tail = n;
    head = n;
    ++num_elements;
}

void list::swap(list &other)
{
    std::swap(head, other.head);
    std::swap(tail, other.tail);
    std::swap(num_elements, other.num_elements);
}

答案 1 :(得分:0)

也许这会帮助您:

template<typename T>
class List {
private:
    Node<T>* head = nullptr;
    Node<T>* tail = nullptr;

    std::size_t _size = 0;

public:

    List() = default;

    // copy constructor
    List(const List<T>& l) {
        _size = l._size;
        Node<T>* current = nullptr;
        Node<T>* previous = nullptr;
        for (std::size_t i = 0; i < l._size; ++i) {
            current = new Node<T>(l[i].data);
            current->prev = previous;
            if (previous) {
                previous->next = current;
            } else {
                head = current;
            }
            previous = current;
        }
        tail = current;
    }

    // assignment operator
    List<T>& operator=(const List<T>& l) {
        if (l.isEmpty()) {
            this->clear();
            return *this;
        }

        // keeps existing nodes intact, and only changes their value
        while (_size > l.size()) {
            Node<T>* prev = tail->prev;
            delete tail;
            prev->next = nullptr;
            tail = prev;
            --_size;
        }

        Node<T>* temp = head;
        Node<T>* tempL = l.head;
        for (std::size_t i = 0; i < _size; ++i) {
            temp->data = tempL->data;
            temp = temp->next;
            tempL = tempL->next;
        }

        while (_size < l._size) {
            this->append(tempL->data);
            tempL = tempL->next;
            ++_size;
        }

        return *this;
    }

    ~List() {
        Node<T>* temp = head;
        while (temp) {
            Node<T>* next = temp->next;
            delete temp;
            temp = next;
        }
    }

    void append(const T& value) {
        auto* temp = new Node<T>(value);
        if (!head) {
            // no head also means no tail
            head = temp;
            tail = temp;
        } else {
            tail->next = temp;
            temp->prev = tail;
            tail = temp;
        }
        ++_size;
    }

    void prepend(const T& value) {
        auto* temp = new Node<T>(value);
        temp->next = head;
        if (head) {
            head->prev = temp;
        }
        head = temp;
        ++_size;
    }
};

也就是说,您可能应该遵循“三规则”并实现一个复制构造函数和赋值运算符。