Search code examples
c++valgrind

"blocks are indirectly lost in loss record.." valgrind error - cpp


I am trying to figure out what is wrong with my cpp code, and i need your help!

valgrind Output: enter image description here

the error occurs here (in the test file): list = list.apply(getLen);

the function getLen:

string getLen(string str)
{
    return std::to_string(str.length());
}

the generic function apply:

template<class T>
    template<class Operation>
    SortedList<T> SortedList<T>::apply(Operation operation) const{//TODO: 6 blocks are indirectly lost.

        Node<T>* current_node_to_check = head;
        SortedList<T> new_sorted_list;

        for (int i = 0; i < list_length; ++i) {
            T result = operation(current_node_to_check->info);
            new_sorted_list.insert(result);
            current_node_to_check = current_node_to_check->next;
        }

        return new_sorted_list;
    }

the Node struct implementation:

template<class T>
    struct Node{
        T info;
        Node<T>* next;

        explicit Node(const T& new_info) : info(new_info), next(nullptr){}

    };

the test file:

#include <iostream>
#include "SortedList.h"
#include "examDetails.h"

using std::cout;
using std::endl;
using std::string;
using namespace mtm;

#define TEST(num) cout << endl << "TEST " << (num) << endl;

string getLen(string str)
{
    return std::to_string(str.length());
}

bool isTrollLink(const ExamDetails& exam) {
    return (exam.getLink().find("tinyurl") != string::npos);
}

template<class T>
void printList(SortedList<T> list) {
    for (auto it = list.begin(); !(it == list.end()); ++it) {
        cout << *it << endl;
    }
    cout << endl;
}

int main()
{
    .
    .
    .

    TEST("1.5")
    SortedList<string> lst1 = SortedList<string>();
    lst1.insert("Charlie");
    lst1.insert("Bob");
    lst1.insert("Alice");
    lst1.insert("Donald");

    printList(lst1);

    TEST("1.6")
    SortedList<ExamDetails> lst2;
    lst2.insert(exam1);
    lst2.insert(exam2);

    printList(lst2);

    TEST("1.7")
    SortedList<string> lst3 = lst1;
    printList(lst3);

    TEST("1.8")//TODO: 6 blocks are indirectly lost.
    lst3 = lst3.apply(getLen);
    printList(lst3);


    TEST("1.9")
    lst3.remove(lst3.begin());
    printList(lst3);

    TEST("1.10")
    SortedList<ExamDetails> lst4 = lst2.filter(isTrollLink);
    printList(lst2);
    cout << "----------" << endl;
    printList(lst4);

    return 0;
}

the generic class file:


#ifndef NEW_SORTED_LIST_SORTEDLIST_H
#define NEW_SORTED_LIST_SORTEDLIST_H

#include <stdexcept>

using std::cout;
using std::endl;

namespace mtm {

    template<class T>
    struct Node{
        T info;
        Node<T>* next;

        explicit Node(const T& new_info) : info(new_info), next(nullptr){}

    };

    template<class T>
    class SortedList {
    private:
        Node<T>* head;
        int list_length;
        static const int empty = 0;
        void deleteNodes();

    public:

        class const_iterator;

        SortedList();

        ~SortedList();

        SortedList(const SortedList &to_copy);

        SortedList& operator=(const SortedList &to_assign);

        void insert(const T &to_insert);

        void remove(const const_iterator& iterator);

        int length() const;

        template<class Condition>
        SortedList filter(Condition condition) const;

        template<class Operation>
        SortedList apply(Operation operation) const;



        const_iterator begin() const;
        const_iterator end() const;

    };

    template<class T>
    class SortedList<T>::const_iterator{

        const SortedList<T>* sortedListPointer;
        int index;

        const_iterator(const SortedList<T>* sortedListPointer, int index) : sortedListPointer(sortedListPointer),
                                                                            index(index){}
        friend class SortedList<T>;
        static const int first = 1;

    public:

        const_iterator(const const_iterator&) = default;

        const_iterator& operator=(const const_iterator&) = default;

        const T& operator*(){
            if (index > sortedListPointer->length()+1 || index<0){
                throw std::out_of_range("error: iterator out of range");//TODO
            }
            Node<T>* current = sortedListPointer->head;
            for (int i = 1; i < index; ++i) {
                current = current->next;
            }
            return current->info;
        }

        const_iterator& operator++() {

            ++index;
            if (index > sortedListPointer->length()+1){
                throw std::out_of_range("error: iterator out of range");//TODO
            }
            return *this;
        }

        const_iterator operator++(int){

            SortedList<T>::const_iterator result = *this;
            ++*this;
            return result;
        }

        bool operator==(const const_iterator& it) const{

            return (index == it.index);
        }

    };

    template<class T>
    typename SortedList<T>::const_iterator SortedList<T>::begin() const{
        return const_iterator(this, const_iterator::first);
    }

    template<class T>
    typename SortedList<T>::const_iterator SortedList<T>::end() const{
        return const_iterator(this, this->length()+1);
    }

    template<class T>
    SortedList<T>::SortedList() : head(nullptr), list_length(empty){}

    template<class T>
    SortedList<T>::~SortedList() {

        deleteNodes();
    }

    template<class T>
    SortedList<T>::SortedList(const SortedList& to_copy){

        Node<T>* current_to_copy = to_copy.head;
        Node<T>* current = nullptr;
        Node<T>* new_head;

        if (to_copy.list_length == 0){
            return;
        }else{
            Node<T>* new_node = new Node<T>(current_to_copy->info);
            //exception
            //new_node->info = current_to_copy->info;
            //new_node->next = nullptr;

            current = new_node;
            current_to_copy = current_to_copy->next;
            new_head = current;
        }
        while (current_to_copy){

            Node<T>* new_node = new Node<T>(current_to_copy->info);
            //exception
            //new_node->info = current_to_copy->info;
            //new_node->next = nullptr;

            current->next = new_node;
            current = current->next;
            current_to_copy = current_to_copy->next;
        }
        list_length = to_copy.list_length;
        head = new_head;
    }

    template<class T>
    void SortedList<T>::deleteNodes(){

        if (list_length == empty){
            head = nullptr;
            return;
        }
        Node<T>* current = head->next;
        for (int i = 1; i < list_length; ++i) {
            Node<T>* previous = head;
            Node<T>* temp = current->next;
            delete current;
            current = temp;
            previous->next = current;
        }
        delete head;
        head = nullptr;
        list_length = empty;
    }

    template<class T>
    SortedList<T>& SortedList<T>::operator=(const SortedList &to_assign){

        if (this == &to_assign){
            return *this;
        }

        head = nullptr;
        Node<T>* current_to_assign = to_assign.head;
        Node<T>* current;
        Node<T>* new_head;

        if (current_to_assign){
            Node<T>* first_node = new Node<T>(current_to_assign->info);
            current = first_node;
            current_to_assign = current_to_assign->next;
            new_head = first_node;
        }else{
            deleteNodes();
            head = nullptr;
            return *this;
        }

        while (current_to_assign){

            Node<T>* new_node = new Node<T>(current_to_assign->info);

            current->next = new_node;
            current = current->next;
            current_to_assign = current_to_assign->next;
        }

        head = new_head;
        list_length = to_assign.list_length;
        return *this;
    }

    template<class T>
    void SortedList<T>::insert(const T &to_insert){

        Node<T>* new_node = new Node<T>(to_insert);

        Node<T>* current = head;
        if (!current){
            head = new_node;
            list_length++;
            return;
        }
        Node<T>* previous = head;
        if (current->info < to_insert){
            current = current->next;
        }else{
            new_node->next = current;
            head = new_node;
            list_length++;
            return;
        }
        while (current){
            if (current->info < to_insert){
                current = current->next;
                previous = previous->next;
            }else{
                new_node->next = current;
                previous->next = new_node;
                list_length++;
                return;
            }
        }

        previous->next = new_node;
        list_length++;
    }

    template<class T>
    void SortedList<T>::remove(const SortedList<T>::const_iterator& iterator){

        if (list_length == 1){
            list_length--;
            delete head;
        }

        int index = iterator.index;
        if (index == 1){
            Node<T>* temp = head->next;
            delete head;
            head = temp;
            list_length--;
            return;
        }
        Node<T>* current = head;
        Node<T>* previous = nullptr;
        for (int i = 1; i < index-1; ++i) {

            if ((i+1) == index-1){
                previous = current;
            }
            current = current->next;
        }

        previous->next = current->next;
        delete current;//TODO destructor(?)
        list_length--;
    }

    template<class T>
    int SortedList<T>::length() const{
        return list_length;
    }

    template<class T>
    template<class Condition>
    SortedList<T> SortedList<T>::filter(Condition condition) const{

        Node<T>* current_node_to_check = head;
        SortedList<T> new_sorted_list;

        for (int i = 0; i < list_length; ++i) {
            if (condition(current_node_to_check->info)){
                new_sorted_list.insert(current_node_to_check->info);
            }
            current_node_to_check = current_node_to_check->next;
        }
        return new_sorted_list;
    }

    template<class T>
    template<class Operation>
    SortedList<T> SortedList<T>::apply(Operation operation) const{//TODO: 6 blocks are indirectly lost.

        Node<T>* current_node_to_check = head;
        SortedList<T> new_sorted_list;

        for (int i = 0; i < list_length; ++i) {
            T result = operation(current_node_to_check->info);
            new_sorted_list.insert(result);
            current_node_to_check = current_node_to_check->next;
        }

        return new_sorted_list;
    }

}

#endif //NEW_SORTED_LIST_SORTEDLIST_H

without the test 1.8, the code works great with no errors at all! What should I do to fix it? thanks


Solution

  • You allocated a SortedList<string> object in main() which allocated memory and never freed it.

    The other blocks belong to the std::string objects inside the list. They are only lost because the list holding them lost track of them.

    When doing your analysis, don't worry about the 6 indirectly lost blocks, focus on the one directly lost one.

    Almost certainly this is a violation of the "Rule of Five". Your SortedList does an allocation in its constructor and doesn't properly manage that allocation in its move-assignment operator.


    Looking at your code in the copy assignment operator, this line is causing the leak

    head = nullptr;
    

    It shouldn't be needed, you later call deleteNodes() which will do that anyway, and by erasing head beforehand you prevent deleteNodes() from doing its work.