Destructor for LinkedList of LinkedLists

时间:2015-10-30 23:56:17

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

For an assignment in one of my programming classes we have to make an adjacency list that is a linked list of linked lists that would look like this. A->B->C ↓ B->A->D ↓ C->D ↓ D->A->B->C I'm having trouble with a memory leak when trying to free up the memory allocated in the destructor. I've been trying to figure it out for a while now but haven't found/come up with any solutions that work. Also, please ignore that the implementation is included in the header file. We were told it was ok to do for this assignment. Valgrind Error Message: ==2316== HEAP SUMMARY: ==2316== in use at exit: 48 bytes in 2 blocks ==2316== total heap usage: 3 allocs, 1 frees, 64 bytes allocated ==2316== ==2316== 48 (32 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 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== LEAK SUMMARY: ==2316== definitely lost: 32 bytes in 1 blocks ==2316== indirectly lost: 16 bytes in 1 blocks ==2316== possibly lost: 0 bytes in 0 blocks ==2316== still reachable: 0 bytes in 0 blocks ==2316== suppressed: 0 bytes in 0 blocks ==2316== ==2316== For counts of detected and suppressed errors, rerun with: -v ==2316== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) Here is some of the code I'm using (compiled with gcc c++11): linkedlist.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; }

2 个答案:

答案 0 :(得分:2)

Not all paths of get(location) return a value. Use your compiler's warnings to find issues like these (e.g. -Wall -Wextra -pedantic). Also, make sure you initialize all members at construction. struct Node { T data {}; Node<T>* next = nullptr; }; and Node<T>* head = nullptr; Node<T>* tail = nullptr; Node<T>* curr = nullptr; UPDATE After reviewing more closely, indeed you never delete the pointer that is the data T in your outer list. Since your list doesn't know whether the T is an (owned) pointer, it cannot decide to delete. Usually, we use smart pointer wrappers to address this. In your case you may not "be allowed" to use that, so, write the extra loop to delete the data pointers before deleting the list nodes. Suggested Fix Here's a reworked example that optionally takes a NodeFree type template argument, so you can pass in std::default_delete<T> for your outer list: 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); } }; So you can use it like: typedef LinkedList<int> inner; LinkedList<inner*, std::default_delete<inner> > matrix; matrix.insertAtTail(new LinkedList<int>); matrix.get(0)->insertAtTail(6); And the leak is gone. Live Demo 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); }

答案 1 :(得分:-1)

Ok from my experience, when you use templates, you can implement them in the same header file but you have to separate the implementation from the declaration. A common solution to this is to write the template declaration in a header file and include the implementation at the end of the header. Read the top vote here Why can templates only be implemented in the header file? I'm not sure if this is your problem but last time I tried implementing the templates the way you do, my program just didn't work at all.