I am trying to remove duplicates from a linked list, and encountered a problem, which is probably obvious and straightforward but I haven\'t used C++
in many years
So why I am not allowed to delete something that was newed somewhere else? Is it because it was newed outside the current scope?
You're allowed to delete
things new
ed elsewhere. This is not a problem as long as you're pointing to a valid address. The seg-fault shows up when you then try to use one of the pointers that was pointing to the now-delete
ed address.
Second, freeing the space works fine, but I feel that's like cheating!
You should only use free
when you've used malloc
and delete
when you've used new
. You should never mix them up. Also, after you delete
, get into the habit of assigning NULL
to the pointer variable.
Because I may need things to be done on my Node's destructor.
The class encapsulating the data should also do its own memory management internally. For example, the LinkedList
should manage the memory of the nodes.
This means that, if you have something similar to LinkedList<T>::add(Node<T> *data)
, you should instead consider something like LinkedList<T>::add(T data)
; then the list class itself takes care of handling the nodes.
Otherwise, you run the risk of sending a new
ed node into the list, then the list delete
s it internally at some point, but somewhere else, the list's client still holds a reference to a now-invalid node. When the client tries to use it, bam!: seg-fault again.
What am I doing wrong? How to properly kill that removed node?
Besides mixing the new
s with free
s, the segfault is likely being triggered by an invalid memory access, so you should find the pointer that is no longer pointing to valid (i.e. undeleted
) memory.
There are many answers stating that new/delete
and malloc()/free()
should always be paired so, but I feel that it should be said why exactly.
As in your initializations,
Node<char> *h = new Node<char>('H');
the new
keyword returns a fully typed object object pointer, defined in the statement itself, while malloc()
simply allocates a given memory space without assigning a type, and so returns a void*
. Additionally, new/delete
deal with memory from the Free Store while malloc()/free()
allocate/free memory on the Heap, although some compilers might implement new/free
in terms of malloc()/free
in some instances.
Also of some importance is that new
and delete
call the constructor and destructor respectively, while malloc()
and free()
simply allocate and deallocate heap memory, with no regard for the state of the memory itself.
This is an addendum to other answers which correctly identify and solve the OP's immediate problem. A quick walk-though is required to explain what will happen next.
Node<T> *temp = cur;
prev->next = cur->next;
cur = cur->next;
cur->prev = prev;
At this point temp has not been cleared so next and prev still point to the two nodes that used to surround cur and are now linked together. This will cause serious problems if not for:
free(temp); //Here is my problem!!!
free
fails because the value pointed to by temp was allocated by new. NathanOliver's answer has this covered, but lurking beneath is what will happen with
delete temp;
This will invoke the destructor to clean up like a good little object. Unfortunately the Node
destructor looks like this:
~Node() { delete next; delete prev; }
temp->next
still points to a live node. So does temp->prev
.
Next and prev get delete
d. Which calls their destructors, delete
ing the two nodes they touch, and invokes a deathstorm that will destroy all of the Nodes in the list.
If the double-deletes don't kill the program first. Each linked node will try to delete
the node that just deleted
them. Not good.
Odds are pretty good that the Node
destructor should just look after itself and leave the destruction of the other Node
s to the LinkedList
class.
In this case having the code of the constructor and also of destructor is very important.
By default you should use "new" in constructor and "delete" in destructor. To be more specific, is very important to allocate all the resources you need in constructor and deallocate in destructor, this way you ensure you don't have any memory leaks.
free(temp);//You don't need this, also you don't need delete.
Please post your constructor and your destructor code.
L.E:
I think you should do something like this:
template<class T>
class LinkedList
{
private:
struct Node {
T m_data;
Node *m_next;
Node *m_prev;
};
Node* m_first;
Node* m_last;
int m_count;
public:
LinkedList();
~LinkedList();
T GetFirst();
T GetLast();
void AddNode(T node);
void RemoveAt(int index);
T GetAt(int index);
int Count();
};
//constructor
template <typename T>
LinkedList<T>::LinkedList(){
m_count = -1;//-1 == "The list is empty!
}
template <typename T>
void LinkedList<T>::AddNode(T data){
Node* temp = new Node; //new is called only here -> delete is called in destructor or in RemoveAt
temp->m_data = data;
if (m_count > -1){//If the list contains one or more items
temp->m_next = m_first;
temp->m_prev = m_last;
m_last->m_next = temp;
m_last = temp;
m_count++;
}
else if (m_count == -1)
{//If no items are present in the list
m_first = temp;
m_first->m_next = temp;
m_first->m_prev = temp;
m_last = temp;
m_last->m_next = temp;
m_last->m_prev = temp;
m_count = 0;
}
}
template <typename T>
void LinkedList<T>::RemoveAt(int index){
if (index <= m_count)//verify this request is valid
{
Node* temp = m_last;
for (int i = 0; i <= index; ++i){
temp = temp->m_next;
}
temp->m_prev->m_next = temp->m_next;
temp->m_next->m_prev = temp->m_prev;
delete temp;
m_count--;
}
}
template <typename T>
T LinkedList<T>::GetAt(int index)
{
Node* temp = m_first;
if (index <= m_count && index > -1){
int i = 0;
while(i < index){
temp = temp->m_next;
i++;
}
}
return temp->m_data;
}
template <typename T>
T LinkedList<T>::GetFirst() {
return m_first->data;
}
template <typename T>
T LinkedList<T>::GetLast(){
return m_last->data;
}
template <typename T>
int LinkedList<T>::Count(){
return m_count;
}
template <typename T>
LinkedList<T>::~LinkedList(){
while(m_count > -1){
Node* temp = m_first;
m_first = temp->m_next;
delete temp;//delete in destructor
m_count--;
}
}
You cannot use free()
if you allocated memory with new
You can use malloc()
with free()
or new
with delete
. You also should not use malloc()
and free()
with objects as they do not call the objects constructor and destructor unlike new
and delete
So since you allocated the nodes with new
we can change
free(temp); //Here is my problem!!!
to
delete temp;