3

MySinglyLinkedList.h:

#include <iostream>

template<class T> class LinkedList;

template<class T>
class LinkedNode {   
 public:
  LinkedNode(T new_data):data(new_data) {; }

 private:
  friend class LinkedList<T>; 
  LinkedNode<T> *next;
  T data;
};

template<class T>
class LinkedList {   
 public:
  LinkedList();
  ~LinkedList();
  void PushNode(T new_data);
  void Delete(LinkedNode<T> *pnode);
  void Show();

 private:
  LinkedNode<T> *head; //Head pointer
  LinkedNode<T> *tail; //Tail pointer
  int length;          //Length of the list
};

//Initialize an empty list when creating it
template<class T>
LinkedList<T>::LinkedList()
{
  head = tail = NULL;
  length = 0;
}

//delete all the nodes when deconstructing the object
template<class T>
LinkedList<T>::~LinkedList()
{
  LinkedNode<T> *ptr = head;
  while (ptr)
  {
    LinkedNode<T> *ptr_del = ptr;
    ptr = ptr->next;
    Delete(ptr_del);
  }
}

//Add one node to the tail of the list
template<class T>
void LinkedList<T>::PushNode(T new_data)
{
  LinkedNode<T> *pnew_node = new LinkedNode<T>(new_data);
  pnew_node->next = NULL;
  if (!length) {
    head = tail = pnew_node;
    length++;
  } else {
    tail->next = pnew_node;
    tail = pnew_node;
    length++;
  }
}

//Delete the node pointed by pnode
template<class T>
void LinkedList<T>::Delete(LinkedNode<T> *pnode)
{
  LinkedNode<T> *ptr = head;
  if (pnode==head) {
    head = pnode->next;
  } else {
    while(ptr->next != pnode)
    {
      ptr = ptr->next;
    }    
    ptr->next = pnode->next;
  }
  if(pnode == tail)
     tail = ptr;

  delete pnode;
  length--;
}

template<class T>
void LinkedList<T>::Show()   //Print all the contents in the list
{
  LinkedNode<T> *pnode = head;
  while(pnode)
  {
    std::cout << pnode->data << std::endl;
    pnode = pnode->next;
  }
  std::cout << "In total: " << length << std::endl;  
}

The main function is as follows:

#include "MySinglyLinkedList.h"
#include <cstdlib>
#include <ctime>

using namespace std;

int main(int argc, char *argv[])
{
  //list_len is the length of the list
  int list_len = 5;
  srand(time(0));
  if (argc > 1)
    list_len = atoi(argv[1]);

  LinkedList<int> test_list;   //Create the first list: test_list

  for (int i = 0; i < list_len; i++)
  {
    //The elements in the list are random integers
    int cur_data = rand()%list_len;      
    test_list.PushNode(cur_data);
  }
  test_list.Show();

  LinkedList<int> test2 = test_list;  //Create the second list: test2
  test2.Show();

  return 0;
}

Since I didn't define any copy constructor here, the default copy constructor will be called and do the bit copy when creating the second list, and as a result test_list and test2 will point to the same linked list. Therefore the first node of the list will be deleted twice when the two object are deconstructed. But the fact is nothing wrong happened when I compiled the program using GCC, and it ran successfully in Linux. I don't understand why no error occurred.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
eaglesky
  • 730
  • 2
  • 13
  • 28
  • As a side note: The compiler generated copy constructor does not do a **bit copy**. It calls each members copy constructor (in the order they are declared in the class). Non class types (int/float etc) are done by assignment. – Martin York Jan 12 '13 at 07:08

2 Answers2

6

Deleting a pointer that has already been deleted causes undefined behavior, so anything can happen. If you want to make sure nothing happens, set the pointer to null after deletion. Deleting null does nothing, and it won't cause an error.

See: c++ delete (wikipedia)

Diego Basch
  • 12,764
  • 2
  • 29
  • 24
  • I understood the question as *why* does the compiler not complain rather what happens when you delete something twice. – JBentley Jan 12 '13 at 03:01
  • compiler does not manages memory allocated from HEAP (malloc/free, new/delete). It just calls the right routine to delete the object(s). –  Jan 12 '13 at 03:06
  • @JonBentley: How can the compiler warn about runtime actions? – Martin York Jan 12 '13 at 07:09
  • @Loki The original question clearly says "But the fact is nothing wrong happened when I compiled the program using GCC", so part of the answer he is looking for is a reason why the compiler doesn't generate an error when you try to delete a pointer twice. I don't follow why you're asking me how the compiler can warn about runtime actions? – JBentley Jan 12 '13 at 11:00
  • @JonBentley: Because of your interpretation of the question. If that is your interpretation of the question you should be commenting on the question asking why. – Martin York Jan 13 '13 at 04:30
  • @Loki I'm afraid I don't follow. I should be asking him why he's asking that question? – JBentley Jan 13 '13 at 06:04
0

As per Diego's answer, the C++ standard permits deleting NULL pointers. The compiler has no way to know what value your pointer will contain when you delete it the second time (i.e. it might be NULL), therefore it has no choice but to allow it in order to comply with the standard.

JBentley
  • 6,099
  • 5
  • 37
  • 72
  • It seems that I didn't describe my question clearly.. I think when deleting a memory twice, there should be some error when the program runs. But the fact is that the program runs without any error. That's what I don't understand. – eaglesky Jan 12 '13 at 06:33
  • @eaglesky It's undefined behaviour. Like a buffer overrun, you are likely corrupting some portion of memory, but that doesn't necessarily mean there will be any noticeable consequences unless some code subsequently runs which is dependent on the (now) corrupted memory. If that doesn't happen by the time your program completes, then you will not notice anything. – JBentley Jan 12 '13 at 06:46
  • @eaglesky Have a look at this question: http://stackoverflow.com/questions/2746677/what-happens-when-you-deallocate-a-pointer-twice-or-more-in-c – JBentley Jan 12 '13 at 06:47
  • @eaglesky: The compiler writers could get the runtime to do the check. But there would be a cost associated with the extra protection. But why should I pay for the cost (my code works) just to make your life easier. One of the principles of C and C++ is not to force people to pay for stuff they don't want. But some compilers do actual do the check. MSVC in debug mode will use a very different runtime from release that does do this kind check. – Martin York Jan 12 '13 at 07:15