Reputation: 1006
template<typename T>
T& List<T>::popFront()
{
if (head == NULL)
throw std::underflow_error("List error: no head node available\n");
if (tail == head)
tail = NULL;
T item = head->item;
head = head->next;
return item;
}
I have a recursive struct ListNode with the following fields:
template <typename T>
struct ListNode {
T item;
ListNode* next;
...
}
The problem is that I want to deallocate a head node after popFront procedure, but as all the nested nodes are indirectly pointing to the same address, their addresses also vanish from the heap. So for now as you can see above I just alter the pointer address of a head node to the next one, which I believe leads to a memory leak.
I do not exclude that I'm absolutely wrong with this approach and my assumption. Please, consider the most efficient way to perform such task, if this deallocation is really necessary.
Upvotes: 0
Views: 129
Reputation: 1299
There are a couple of problems here.
template<typename T>
T& List<T>::popFront()
{
if (head == NULL)
throw std::underflow_error("List error: no head node available\n");
if (tail == head)
tail = NULL;
T item = head->item;
head = head->next; //Memory leak, you just lost your only pointer to the head item
return item; //Returning reference to stack variable, undefined behavior
}
I would suggest that you change the signature to return by value, so that you can return a local and deallocate the element in the heap.
template<typename T>
T List<T>::popFront()
{
if (head == NULL)
throw std::underflow_error("List error: no head node available\n");
if (tail == head)
tail = NULL;
T item = head->item;
ListNode* old_head = head; //keep this for deallocation
head = head->next;
delete old_head; //Deallocate the old head
return item; //Return by value
}
You could, of course, adopt the behavior of std::list
and have different methods for access and popping, front()
and pop_front()
respectively.
As per the signature, front()
returns a reference, which is far more efficient if T
is a heavy object.
This is all assuming you're doing it for academic purposes, of course. Otherwise, well, use std::list
or a similar standard library container.
Upvotes: 2
Reputation: 3321
When you pop the head, before deleting it, grab the next pointer and make it head.
template<typename T>
T List<T>::popFront()
{
if (head == NULL)
throw std::underflow_error("List error: no head node available");
if (tail == head)
tail = NULL;
T item = head->item;
ListNode *next = head->next; // Grab next
delete head; // Now it is safe to delete head
head = next; // Head now points to next
return item;
}
Is the tail needed here? Why not just have next of head equal to NULL
when there is only one node?
EDIT: Just noticed. You're not supposed to return a reference to Item
because we're destroying the ListNode
that was holding it. Return by value. Alternatively you can return ListNode*
rather than deleting it, but this is probably not how you want List
's interface to work.
Upvotes: 1