next up previous contents
Next: The Merge Sort Code: Up: The Source: charsllist.c Previous: Overloaded Input and Output

The Constructor/ Destructor:

    // ***************************************************** //
    // Explicit constructor for CHAR_SL_CELL class            //
    // ***************************************************** //
 
CHAR_SL_CELL::CHAR_SL_CELL(char *name_in)
{
  name = new char[strlen(name_in)+1];
  strcpy(name,name_in);
  next = NULL;
}

Note that when a CELL is constructed, we set the next pointer to NULL, create a char vector to hold the passed in name string and we do nothing for the field element TYPE element. We insert a large number of specialized debugging print statements into our destructor so that we can track the deletion of objects as they go out of scope. We want the diagnostic messages about entering and leaving such a call to be indented apprpriately to show the recursive nature of the call to the CHAR_SL_CELL destructor. We handle this messiness by introducing the static variable count. Each recursive call spaces the indentation level over by 4 spaces. So in application code, if the diagnostics are turned on, we see nicely nested calls to the destructors. This makes it a lot easier to see the recursive nature of the call. The basic destructor code (sans diagnostics) is as follows:

CHAR_SL_CELL::~CHAR_SL_CELL()
{
  if(name!=NULL){
    delete [] name;
    }
  if(next!=NULL)
    delete next;
}

and a version with lots of diagnostic prints is as follows:

    // ********************************************** //
    // destructor for CHAR_SL_CELL class       //
    // ********************************************** //
 
CHAR_SL_CELL::~CHAR_SL_CELL()
{
  static int count = -1;
  if(SCREEN_DIAGNOSTIC){
    for(int j=0;j<4*(count+2)+1;++j)
      cout << " ";
    cout << "In CHAR_SL_CELL<" << name << "> destructor: ~next." << endl;
    }
  if(next!=NULL){
    if(SCREEN_DIAGNOSTIC){
      for(int j=0;j<4*(count+2)+1;++j)
        cout << " ";
      cout << "  Calling destructor " << count << " : ~next." << endl;
      }
    count++;
    if(name!=NULL)
      delete [] name;
    if(next!=NULL)
      delete next;
    }
  count = -1;
}

Now it is very important to look carefully at this code. The diagnostic code is there just to format the destructor calls in an nicely indented fashion-check it out and see if you understand it!-but the bottom line is that when the CELL destructor is called, the name field is deleted and then the next pointer. Now the line delete next; recursively calls the CELL destructor. This call does not end until the next pointer is NULL. To see this in action, let's jump ahead to a SLLIST object, which consists of many CELLs. What happens when its destructor is called?

Here is the CHAR_SLLIST constructor/ destructor code:

    // ********************************************** //
    // default constructor for CHAR_SLLIST class      //
    // ********************************************** //
CHAR_SLLIST::CHAR_SLLIST(char *name_in)
{
  name = new char[strlen(name_in)+1];
  strcpy(name,name_in);

  head = NULL;
}

Note that the default constructor for the list sets the head pointer to NULL. So a list starts off with nothing in it at all.

    // ********************************************** //
    // destructor for CHAR_SLLIST class               //
    // ********************************************** //
 
CHAR_SLLIST::~CHAR_SLLIST()
{
  if(head!=NULL)
    delete head;  
}

This is very compact and when the list is destroyed with its destructor call, the line delete head; is a call to the CELL destructor which then proceeds to call itself recursively until it reaches the last cell in the list which has a NULL next pointer. At that point, the call to the LIST destructor is done:

Here is a typical application ( we are jumping ahead with code from the list class, but it should be accessible. If necessary, look forward a few pages to get the gist of it):

int main(void)
{
  char input[10] = {'2','2','3','z','u','q','Q'};

  cout << "Instantiating List Qaitlin" << endl;
  CHAR_SLLIST Qaitlin("charQa1");

  cout << endl << "Building Qaitlin List" << endl;
  Qaitlin.build_list(input,7);
  cout << "Qaitlin has " << Qaitlin.get_number_elements() << " elements";
  cout << endl;
  cout << endl << "Qaitlin = " << endl << Qaitlin << endl;
  return(0);
}

The run-time output without diagnostics turned is:

albert% sllists
Instantiating List Qaitlin

Building Qaitlin List
Qaitlin has 7 elements

Qaitlin = 
2-->2-->3-->z-->u-->q-->Q.

The run-time output with disgnostics turned on shows the constructor-destructor chains for the list object Qaitlin:

albert% sllists
Instantiating List Qaitlin
In default CHAR_SLLIST<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      Here the single CELL
                                                   for the LIST constructor
                                                   is created
Exit explict CHAR_SL_CELL<charQa1> constructor.                 
Exit default CHAR_SLLIST<charQa1> constructor.

Building Qaitlin List                              Need 7 CELLs
In CHAR_SLLIST<charQa1> build_list.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 0
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 1
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 2
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 3
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 4
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 5
Exit explict CHAR_SL_CELL<charQa1> constructor.
In explict CHAR_SL_CELL<charQa1> constructor.      CELL 6
Exit explict CHAR_SL_CELL<charQa1> constructor.
Exit CHAR_SLLIST<charQa1> build_list.
Qaitlin has 7 elements

When we go out of scope, we see the following destructor calls:

Qaitlin = 
2-->2-->3-->z-->u-->q-->Q.

Entering CHAR_SLLIST<charQa1> destructor.
     IN SL_LIST: Removing CHAR_SL_CELL -1
     In CHAR_SL_CELL<charQa1> destructor: ~next.
       Calling destructor -1 : ~next.                   CELL 0 destroyed
         In CHAR_SL_CELL<charQa1> destructor: ~next.
           Calling destructor 0 : ~next.                 CELL 1 destroyed
             In CHAR_SL_CELL<charQa1> destructor: ~next.
               Calling destructor 1 : ~next.              CELL 2 destroyed
                 In CHAR_SL_CELL<charQa1> destructor: ~next.
                   Calling destructor 2 : ~next.           CELL 3 destroyed
                     In CHAR_SL_CELL<charQa1> destructor: ~next.
                       Calling destructor 3 : ~next.        CELL 4 destroyed
                         In CHAR_SL_CELL<charQa1> destructor: ~next.
                           Calling destructor 4 : ~next.     CELL 5 destroyed
                             In CHAR_SL_CELL<charQa1> destructor: ~next.
                               Calling destructor 5 : ~next.  CELL 6 destroyed
Leaving CHAR_SLLIST destructor.


next up previous contents
Next: The Merge Sort Code: Up: The Source: charsllist.c Previous: Overloaded Input and Output
Jim Peterson
1999-04-22