Previous Section  < Free Open Study >  Next Section

Selected C++ Example #20


// Chapter 9 Example #2 

// This C++ example illustrates the implementation of garbage

// collection in C++. Garbage collection in C++ typically

// revolves around a memory handler function that ''knows''

// which classes in the system are hoarding memory. The memory

// handler knows which classes are hoarding memory because the

// author of the application overloaded the new and delete

// functions for the class in question.

// In this example, we have a LinkedList class, which contains

// Node objects. The Node class has overloaded its standard

// allocator and deallocator (i.e., operators new and delete)

// to hoard recyclable nodes on a freelist (a class variable)

// The Node class has a free_garbage class method, which users

// of the class can call. In this example, our memory handler,

// called free_garbage, detects that memory has run out and

// tells the Node class to put the memory back.



#include <iostream.h>

#include <new.h>

#include <stdlib.h>

#include <string.h>



typedef int DATUM;



// The memory handler for the application.

void garbage_collect();





// Notice that the Node class is granting friendship to the

// LinkedList class. Granting friendship to a class or method

// is typically considered bad style since it weakens data

// hiding. In this example, it is allowed since the Node class

// is really an internal implementation detail of the LinkedList.

// To force the LinkedList class to use accessor methods for

// each access would overly convolute the code of the LinkedList

// with items such as ''head->set_next(head->get_next())''

// instead of the much more readable ''head = head->next''.

class Node {

     DATUM data;

     Node* next;

     static Node* freelist;

     friend class LinkedList;

public:

     Node(DATUM&);

     void* operator new(size_t);

     void operator delete(void*);

     static int free_garbage();

     static void print_freelist();

};



// The definition of the class variable ''freelist'' attached

//to the Node class.

Node* Node::freelist = NULL;





// The overloaded new operator (the standard allocator) will

// first try to recycle a node before going to the heap and

// getting one the expensive way.



void*

Node::operator new(size_t sz)

{

     Node* p;



// First try to get a node off the freelist.

     if (freelist != NULL) {

          p = freelist;

          freelist = freelist->next;

     }

     else {

// If that doesn't work, get one the old-fashioned, expensive

// way by making a trip to the heap.

           p = (Node*) new char[sz];

      }

      return(p);

}





// Be sure that ''delete 0'' works properly (a NOP),

// then add the node to the freelist for later recycling.

void

Node::operator delete(void* p)

{

     if (p != NULL) {

           ((Node*) p) ->next = freelist;

           freelist = (Node*) p;

     }

}



// The constructor for Node simply initializes the two data

// members, regardless of whether or not it is a recycled node.

Node::Node(DATUM& new_data)

{

     data = new_data;

     next = NULL;

}





// This class method is used for diagnostics. It prints the

// current addresses out on the Node's freelist.

void

Node::print_freelist()

{

     Node* temp = freelist;



     cout << ''Freelist: '';

     while (temp != NULL) {

          cout << temp << '' '';

          temp = temp->next;

     }

     cout << ''\n'';

}



// This class function will throw away the freelist of

// nodes if a garbage-collection handler function

// requests it. Since nodes have an overloaded delete

// operator, the global delete must be called. Note the use

// of the scope resolution operator. This function returns

// the number of bytes it put back on the heap.

int

Node::free_garbage()

{

     Node* temp;

      int counter = 0;

      while (freelist != NULL) {

           temp = freelist;

           freelist = freelist->next;



// Must use global delete to prevent infinite

// recursion between deleting a node off of the

// freelist and the overloaded delete operator putting

// the node back on the freelist.

           ::delete temp;

           counter++;

      }

      return(counter*sizeof(Node));

}





// The LinkedList class is a user of Nodes. Its constructor

// and destructor call the overloaded new and delete of the

// Node class. Use of LinkedList objects results in a Freelist

// of Node objects stashed away in the Node class.

class LinkedList {

     Node* head;

     int length;

public:

     LinkedList();

     LinkedList(DATUM);

     ~LinkedList();

     int insert(DATUM);

     int remove(DATUM);

     void traverse() const;

};



LinkedList::LinkedList()

{

     head = NULL;

     length = 0;

}



LinkedList::LinkedList(DATUM first_item)

{

     head = new Node(first_item);

     length = 1;

}



LinkedList::~LinkedList()

{

     Node* temp;



     while (head != NULL) {

         temp = head;

         head = head->next;

         delete temp;

     }

}

int

LinkedList::insert(DATUM new_item)

{

     Node* temp = head;



     if (temp == NULL) {

          head = new Node(new_item);

     }

     else {

          while(temp->next != NULL) {

                    temp = temp->next;

          }

          temp->next = new Node(new_item);

     }

     return(++length);

}





int

LinkedList::remove(DATUM bad_item)

{

     Node* temp = head;



     if (temp == NULL) {

          return(0);

     }

     if (head->data == bad_item) {

          head = head->next;

          length--;

          delete temp;

     }

     else {

          while (temp->next != NULL&&

                      temp->next->data != bad_item){

                     temp = temp->next;

          }

          if (temp->next == NULL) {

                     return(0);

          }

          else {

                     Node* p = temp->next;

                     temp->next = temp->next->next;

                     length--;

                     delete p;

          }

    } return(1);

}





void

LinkedList::traverse()const

{

      Node* temp = head;



      cout << ''('';

      while (temp != NULL) {

           cout << temp->data << '''';

           temp = temp->next;

      }

      cout << '')\n'';

}





// The main function first installs our memory handler.

// It then works with a number of LinkedList objects, which

// results in a freelist of nodes. We attempt to allocate

// an unallocatable amount of memory. The new operator for

// the array of characters is called. It calls the equivalent

// of malloc, which fails. New detects the failure and checks if

// there is a memory handler. There is one installed

// (garbage_collect), so we execute it and try malloc again,

// hoping for success. The hope is that the garbage collector

// will free enough memory so that the second attempt by new

// to allocate the memory is successful. In this example, the

// second attempt fails. The memory handler detects that no

// additional space has been freed and thus exits from the

// application, preventing infinite attempts to free memory even

// when there is none to free.

void

main()

{

     set_new_handler(garbage_collect);



     LinkedList a;

     LinkedList b(100);



     a.traverse();  b.traverse();

     a.insert(1);   a.insert(2);

     a.insert(3);   a.insert(4);

     a.insert(5);   a.insert(6);

     b.insert(200); b.insert(300);

     b.insert(400); b.insert(500);

     b.insert(600); b.insert(700);



     a.traverse();b.traverse();

     Node::print_freelist();



     a.remove(1); b.remove(100);

     a.traverse();b.traverse();

     a.remove(6); b.remove(700);

     a.traverse();b.traverse();

     a.remove(4); b.remove(400);

     a.traverse();b.traverse();



     Node::print_freelist();

     a.insert(99); a.insert(199);

     b.insert(-45);b.insert(-44);

     a.traverse(); b.traverse();



     Node::print_freelist();



     char* big_buf = NULL;



// This block is impossible to retrieve. New will fail and will

// call our handler, which tells the Node class to free

// its horded nodes. The call to new still fails, so we exit

// the application.



     big_buf = new char[65530L];

     strcpy(big_buf, ''Arthur Riel'');

     cout << ''big_buf = '' << big_buf <<''.\n'';

}





// The garbage-collection routine is a cornerstone in this

// example. It possesses knowledge of what element of the

// application is hoarding memory. When the heap is out of

// space, this routine tells the hoarders to put their memory

// back on the heap. If this routine cannot find any memory to

// free, it bails out.

void

garbage_collect()

{

     int space_freed;



// Get some space freed on the heap by telling the hoarding

// Node class to put its memory back.

      space_freed = Node::free_garbage();



// If there's nothing to free, simply fail. Otherwise,

// return and let new try again.

      if (!space_freed){

           cerr << ''Fatal Memory Error!!!\n'';

           exit(-1);

      }

}

    Previous Section  < Free Open Study >  Next Section