• Welcome to Jose's Read Only Forum 2023.
 

ProgEx32 - Framework For A More Efficient String Class

Started by Frederick J. Harris, November 17, 2009, 09:29:49 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris


/*
  ProgEx32  -- A More Memory Efficient String Class.  C++ Compilation/Linking

  As mentioned in ProgEx31, we are going to try to alter our String Class so it
  isn't always thrashing around in memory allocating/deallocating memory blocks.
  To do that we'll add one more private data member to our class which will keep
  track of the current maximum number of characters the String can hold.  We'll
  name the data member 'iAllowableCharacterCount', and it will be one less than
  whatever the present memory allocation is.  Another change is that we'll
  define an equate named MINIMUM_ALLOCATION.  Unlike in ProgEx31's Uninitialized
  Constructor where no memory allocations occured, in this example a declaration
  like so...

  String s1;

  will cause the Constructor to allocate an amount of memory equal to the above
  equate, which in this program has been set to 8 bytes.  Yet another change is
  that whenever the various member functions determine that not enough memory is
  indicated by this->iAllowableCharacterCount, rather than only re-allocating
  the needed amount, the needed amount will be multiplied by another equate
  named EXPANSION_FACTOR so as to anticipate future String growth.  In this
  program EXPANSION_FACTOR has been set to 2.  Below is an exact copy of our
  String Declaration header showing what our String Class now looks like...

  //Strings.h                         Class Declaration For String Class So Far
  #if !defined(STRINGS_H)             //This is an inclusion guard to guard against other
  #define STRINGS_H                   //files that may have previously included this file.
  #define EXPANSION_FACTOR      2     //This is the factor to anticipate future space needs
  #define MINIMUM_ALLOCATION    8     //Allocations will be at least this number

  class String
  {
   public:
   String();                         //Uninitialized Constructor - Does nothing
   String(const char);               //Constructor Initializes String With char
   String(const char*);              //Constructor Initializes String With char*
   String(const String&);            //Constructor Initializes String With Another String (Copy Constructor)
   String& operator=(const char);    //Operator= for assigning char to String
   String& operator=(const char*);   //Operator= for assigning literal or char* to String
   String& operator=(const String&); //Operator= for assigning 2nd String to 1st
   String& operator+(const char);    //Operator+ for adding char to String
   String& operator+(const char*);   //Operator+ for adding A Null Terminated Ascii String Array
   String& operator+(const String&); //Operator+ for adding Another String To The String
   int LenStr(void);                 //Returns the length of what's pointed to by pStrBuffer
   char* lpStr(void);                //Returns Address of String data Member  >> this->lpStr()
   ~String();                        //String Destructor

   private:
   char* pStrBuffer;                 //This char* member holds the address of an allocated String
   int   iAllowableCharacterCount;   //Max number of chars that will fit in presently allocated buffer
  };
  #endif

  To see the improvement, lets run the example we had in the last ProgEx where
  we concatenated a few letters of the English alphabet together, except this
  time we'll do the whole alphabet.  If we would have done that in the last
  example (ProgEx31) we would have had 26 allocations and 26 releases.  You can
  find out for yourself in the output below how the changes we made affects this
  here.  My comments on the code will follow the example and the output
  immediately beneath...
*/


#include <stdio.h>   //The String declaration below 'String s1' will cause an 8
#include "Strings.h" //byte memory allocation.  The logic in the for loop will
                     //cause calls to the overloaded operator+ member, and that
int main(void)       //member function will check the present allowable char
{                    //count against the present count of chars accumulated in
String s1;          //the String.  Only when the char count reaches 7 will the
                     //function re-allocate memory and release the original 8
for(unsigned int i=65; i<91; i++)  //bytes.  However, it will double the
{                                  //present maximum char count and round to a
     s1=s1+i;                       //16 byte paragraph boundary when it
     printf("\n%s\n",s1.lpStr());   //calculates its new memory allocation size.
}                   //Therefore, the original 8 byte allocation will be doubled
getchar();          //to 16 bytes. The loop will continue running, accumulating
                     //chars, and checking bytes until it reaches 15 chars, and
return 0;           //then it will re-allocate again - but this last time for
}                    //32 bytes.  Therefore, there will only need to be two
                     //two re-allocations after the original 8 byte allocation
                     //to finish the alphabet-not 26 like in our first example!


/*  --Output--

(You'll Need About 325 Lines In Your Console Window Buffer)

Entering Uninitialized String Constructor
  pStrBuffer = 4079520
  this->iAllowableCharacterCount = 7
Leaving Uninitialized String Constructor


Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 0
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = A
Early Exit Leaving String::operator=(const String& strRight)

A

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 1
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = AB
Early Exit Leaving String::operator=(const String& strRight)

AB

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 2
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABC
Early Exit Leaving String::operator=(const String& strRight)

ABC

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 3
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCD
Early Exit Leaving String::operator=(const String& strRight)

ABCD

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 4
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDE
Early Exit Leaving String::operator=(const String& strRight)

ABCDE

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 5
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEF
Early Exit Leaving String::operator=(const String& strRight)

ABCDEF

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 6
  this->iAllowableCharacterCount = 7
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFG
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFG

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 7
  this->iAllowableCharacterCount = 7
  !!!!! Memory Wasn't Adequate.  We Need To Re-Allocate!
  this->iAllowableCharacterCount = 15
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGH
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGH

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 8
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHI
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHI

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 9
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJ
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJ

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 10
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJK
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJK

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 11
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKL
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKL

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 12
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLM
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLM

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 13
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMN
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMN

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 14
  this->iAllowableCharacterCount = 15
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNO
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNO

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 15
  this->iAllowableCharacterCount = 15
  !!!!! Memory Wasn't Adequate.  We Need To Re-Allocate!
  this->iAllowableCharacterCount = 31
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOP
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOP

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 16
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQ
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQ

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 17
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQR
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQR

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 18
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRS
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRS

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 19
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRST
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRST

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 20
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTU
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTU

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 21
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTUV
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTUV

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 22
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTUVW
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTUVW

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 23
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTUVWX
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTUVWX

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 24
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTUVWXY
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTUVWXY

Entering String& String::operator+(const char ch) For Adding A char To A String
  this->LenStr()                 = 25
  this->iAllowableCharacterCount = 31
  Memory Was Adequate.  Didn't Need To Re-Allocate!
Leaving String& String::operator+(const char ch) For Adding A char To A String

Entering operator=(const String& strRight) Assigns Another String To A String
  strRight.pStrBuffer = ABCDEFGHIJKLMNOPQRSTUVWXYZ
Early Exit Leaving String::operator=(const String& strRight)

ABCDEFGHIJKLMNOPQRSTUVWXYZ
*/


/*  ....continued from above

  I'd call this a considerable improvement.  We've cut down the memory alloc-
  ations from 16 down to 3.  Obviously, if MINIMUM_ALLOCATION was set high
  enough the concatenation of the alphabet wouldn't take any more re-allocations
  at all.  However, I suppose this could be construed as cheating!  In any case
  achieving this improvement only made the code a small amount more complicated.
  The way things are now all the member functions that either assign Strings or
  add to them (concatenating operator+ members) will have to compare the
  parameter to the function call with the amount of memory presently held as
  determined by the new iAllowableCharacterCount private member.  If enough
  memory is being held, rather than a re-allocation/de-allocation sequence the
  parameter is simply copied to the existing memory.  If enough memory isn't
  present then a new allocation will be made after multiplying the present need
  by EXPANSION_FACTOR.  For example, allocations could follow a sequence of 8,
  16, 32, 64, 128.... etc.

  String& String::operator+(const char ch)
  {
   int iLen,iNewSize;
   char* pNew;

   iLen=strlen(this->pStrBuffer);
   if(iLen<this->iAllowableCharacterCount)
   {
      this->pStrBuffer[iLen]=ch;
      this->pStrBuffer[iLen+1]='\0';
   }
   else
   {
      iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;
      pNew=new char[iNewSize];
      this->iAllowableCharacterCount=iNewSize-1;
      strcpy(pNew,this->pStrBuffer);
      delete [] this->pStrBuffer;
      this->pStrBuffer=pNew;
      this->pStrBuffer[iLen]=ch;
      this->pStrBuffer[iLen+1]='\0';
   }

   return *this;
  }

  This term...

  iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;

  has an integer division within it that in C/C++ languages ends up usually in
  an integer truncation.  For example, when our String above reached...

  ABCDEFG

  ...and we had this situation...

  this->LenStr()                 = 7
  this->iAllowableCharacterCount = 7

  ...the formula above would have resolved to this...

  iNewSize=((7*2)/16+1)*16;

  ...which due to integer truncation resolves to this...

  iNewSize = 16;
*/

Frederick J. Harris

Strings.h

//Strings.h                         Class Declaration For String Class So Far
#if !defined(STRINGS_H)             //This is an inclusion guard to guard against other
#define STRINGS_H                   //files that may previously included this file.
#define EXPANSION_FACTOR      2     //This is the factor to anticipate future space needs
#define MINIMUM_ALLOCATION    8     //Allocations will be at least this number

class String
{
  public:
  String();                         //Uninitialized Constructor - Does nothing
  String(const char);               //Constructor Initializes String With char
  String(const char*);              //Constructor Initializes String With char*
  String(const String&);            //Constructor Initializes String With Another String (Copy Constructor)
  String& operator=(const char);    //Operator= for assigning char to String
  String& operator=(const char*);   //Operator= for assigning literal or char* to String
  String& operator=(const String&); //Operator= for assigning 2nd String to 1st
  String& operator+(const char);    //Operator+ for adding char to String
  String& operator+(const char*);   //Operator+ for adding char* to String (char* Points To Null Terminated Ascii String Array
  String& operator+(const String&); //Operator+ for adding Another String To The String
  int LenStr(void);                 //Returns the length of what's pointed to by pStrBuffer
  char* lpStr(void);                //Returns Address of String data Member  >> this->lpStr()
  ~String();                        //String Destructor
 
  private:
  char* pStrBuffer;                 //This char* member holds the address of an allocated String
  int   iAllowableCharacterCount;   //Max number of chars that will fit in presently allocated buffer
};
#endif

Frederick J. Harris

Strings.cpp

//Strings.cpp
#include  <stdlib.h>
#include  <stdio.h>
#include  <string.h>
#include  "Strings.h"


/* ****************************************************************************/
// String::String()  This is an uninitialized String Constructor.  It is
// called when a declaration of a String such as this occurs...
//
// String s1;
//
// Note that in this version of the Uninitialized String Constructor it actually
// allocates memory.  The amount it allocates is MINIMUM_ALLOCATION, whatever
// that is set to.  Then it calculates and stores the maximum number of chars
// that can be stored in a buffer of that size taking into consideration the
// need for a final NULL byte.  It stores the maximum allowed char count in
// this->iAllowableCharacterCount.
/* ****************************************************************************/
String::String()    //Uninitialized Constructor
{
printf("Entering Uninitialized String Constructor\n");
pStrBuffer=new char[MINIMUM_ALLOCATION];
pStrBuffer[0]='\0';
printf("  pStrBuffer = %u\n",(unsigned int)pStrBuffer);
this->iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
printf("  this->iAllowableCharacterCount = %u\n",this->iAllowableCharacterCount);
printf("Leaving Uninitialized String Constructor\n\n");
}


/* ****************************************************************************/
// String::String(const char ch)  //Constructor: Initializes with char
//
// This version of an overloaded String constructor would be called by a
// construction such as this...
//
// String s('R');
//
// In C++ this type of construction generalizes to any type of object whatsoever
// so if you want to initialize a simple int this will work...
//
// int iNum(5);
//
// The above will declare an int variable (object) iNum and assign 5 to it.
/* ****************************************************************************/
String::String(const char ch)      //Constructor: Initializes with char
{
pStrBuffer=new char[MINIMUM_ALLOCATION];
pStrBuffer[0]=ch;
pStrBuffer[1]='\0';
iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


/* ****************************************************************************/
// String::String(const char* pStr)  //Constructor: Initializes with char*
//
// This String Class Constructor Overload would be called by a construction such
// as this...
//
// String s1("Hello, World!");
//
// First the string length ( strlen() ) of the parameter is determined, then a
// memory allocation is done where the length is rounded up to the next nearest
// 16 byte paragraph boundary.  Then the char* parameter is copied to the
// storage and the allocated memory size is set in the iAllowableCharacterCount
// private member variable.
/* ****************************************************************************/
String::String(const char* pStr)  //Constructor: Initializes with char*
{
int iLen,iNewSize;

iLen=strlen(pStr);
iNewSize=(iLen/16+1)*16;
pStrBuffer=new char[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
strcpy(pStrBuffer,pStr);
}


/* ****************************************************************************/
// String(const String& s) //Copy Constructor - Initialize with another String
//
// This constructor overload will be triggered by a statement such as this...
//
// String s1 = "Some String";
// String s2(s1);
//
// What you have here is a String being initialized with the contents of a
// second String.  What has to happen is that seperate storage has to be set up
// in the first String so as to accomodate the copying of the second String to
// the first.
/* ****************************************************************************/
String::String(const String& s)  //Constructor Initializes With Another String,
{                                //i.e., Copy Constructor
int iLen,iNewSize;

iLen=strlen(s.pStrBuffer);
iNewSize=(iLen/16+1)*16;
this->pStrBuffer=new char[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
strcpy(this->pStrBuffer,s.pStrBuffer);
}


/* ****************************************************************************/
// String& String::operator=(const char ch)   This version is somewhat differnt
// from the last in that it no longer deletes [] the existing buffer.  The
// existing buffer could be any size really; its length would be stored in
// this->iAllowableCharacterCount.  What this version does is retain the memory
// allocation - whatever it is - and simply stores the parameter char ch at
// offset 0 in the buffer and the required NULL at offset 1.
/* ****************************************************************************/
String& String::operator=(const char ch)  //Overloaded operator = for assigning a character to a String
{
printf("Entering Overloaded operator=(const char ch) For Assigning A char To A String\n");
this->pStrBuffer[0]=ch;
this->pStrBuffer[1]='\0';
printf("Leaving Overloaded operator=(const char ch) For Assigning A char To A String\n\n");

return *this;
}


/* ****************************************************************************/
// String& String::operator=(const char* pStr)    To assign a char* to
// pStrBuffer we first need to determine the length of pStr, then compare that
// length (iLen ) to the allowable char count in this->iAllowableCharacterCount.
// If the former ( pStr ) is less than the latter we can simply copy pStr to the
// String's buffer.  If not, we must allocate a new buffer then delete [] the
// old one.  The term (iLen/16+1)*16 will always round memory requirements up to
// paragraph ( 16 byte ) boundaries.  Once a new allocation is made the para-
// meter is copied to the new buffer and the iAllowableCharacterCount member set
// to the new value.
/* ****************************************************************************/
String& String::operator=(const char* pStr)   //Constructor For If Pointer To
{                                             //Asciiz String Parameter
int iLen,iNewSize;

printf("Entering operator= For Assigning A char*\n");
iLen=strlen(pStr);
if(iLen<this->iAllowableCharacterCount)
    strcpy(pStrBuffer,pStr);
else
{
    delete [] pStrBuffer;
    iNewSize=(iLen/16+1)*16;
    pStrBuffer=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pStrBuffer,pStr);
}
printf("  pStrBuffer = %u\t%s\n",(unsigned int)pStrBuffer,pStrBuffer);
printf("Leaving operator= For Assigning A char*\n\n");

return *this;
}


/* ****************************************************************************/
// String& String::operator=(const String& strRight)
//
// This overloaded operator= member is triggered when an assignment statement
// connects to Strings.  If the String on the right is the same as the one on
// the left, the function exits early by simply returning *this (what's stored
// at this).  Otherwise, the function tests to see if the presently existing
// storage is adequate to store the String passed as a parameter (the one on the
// right side of the assignment statement).  If it is the parameter is simply
// copied to pStrBuffer.  If it isn't new storage is allocated before doing the
// copy, and the new length of the buffer is stored in iAllowableCharacterCount.
/* ****************************************************************************/
String& String::operator=(const String& strRight)  //Overloaded operator = for
{                                                  //assigning another String to
int iRightLen,iNewSize;                           //a String

printf("Entering operator=(const String& strRight) Assigns Another String To A String\n");
printf("  strRight.pStrBuffer = %s\n",strRight.pStrBuffer);
if(this==&strRight)
{
     printf("Early Exit Leaving String::operator=(const String& strRight)\n");
     return *this;
}
iRightLen=strlen(strRight.pStrBuffer);
if(iRightLen < this->iAllowableCharacterCount)
    strcpy(pStrBuffer,strRight.pStrBuffer);
else
{
    iNewSize=(iRightLen/16+1)*16;
    delete [] this->pStrBuffer;
    this->pStrBuffer=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pStrBuffer,strRight.pStrBuffer);
}
printf("Leaving operator=(const String& strRight) Assigns Another String To A String\n\n");

return *this;
}


/* ****************************************************************************/
// String& String::operator+(const char ch)  Overloaded operator + (Adds char)
//
// This member either adds a char to a NULL String or concatenates it onto the
// end of an existing String.  First it checks to see if the length of the
// String in its buffer (this->pStrBuffer) < this->iAllowableCharacterCount.  If
// it is then the char is simply added onto the end of the existing String.  If
// it isn't a larger String is allocated and the existing String is copied to
// it.  Then the existing String is released and the char is added to the end of
// the new String.  Finally, what's stored at this (*this) is returned.
/* ****************************************************************************/
String& String::operator+(const char ch)  //Overloaded operator + (Puts char in
{                                         //String)
int iLen,iNewSize;
char* pNew;

printf("\nEntering String& String::operator+(const char ch) For Adding A char To A String\n");
iLen=strlen(this->pStrBuffer);
printf("  this->LenStr()                 = %u\n",this->LenStr());
printf("  this->iAllowableCharacterCount = %u\n",this->iAllowableCharacterCount);
if(iLen<this->iAllowableCharacterCount)
{
    printf("  Memory Was Adequate.  Didn't Need To Re-Allocate!\n");
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
}
else
{
    printf("  !!!!! Memory Wasn't Adequate.  We Need To Re-Allocate!!!!!\n");
    iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    strcpy(pNew,this->pStrBuffer);
    delete [] this->pStrBuffer;
    this->pStrBuffer=pNew;
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
    printf("  this->iAllowableCharacterCount = %u\n",this->iAllowableCharacterCount);
}
printf("Leaving String& String::operator+(const char ch) For Adding A char To A String\n\n");

return *this;
}


/* ****************************************************************************/
// String& String::operator+(const char* pChar)  -- Adds char* to String
//
// This overloaded operator+ will be called by a construction such as this...
//
// String s1 = "John ";
//
// s1 = s1 + "Jones";
//
// which is equivalent to...
//
// s1.operator+("Jones");
//
// The 1st thing the function does is find out the total length of the existing
// String plus the length of the String to be added, i.e., the parameter pChar.
// If this total length, i.e., iLen, is less than this->iAllowableCharacterCount
// then the code must next determine whether or not anything is already stored
// at this->pStrBuffer.  If pStrBuffer points to a NULL String the parameter
// pChar is simply copied to there.  If something is at pStrBuffer then a
// concatenation ( strcat() ) is in order.  The same basic logic is followed if
// the combined length of both Strings is such that a new and larger memory
// allocation is needed (see the else at mid point).
/* ****************************************************************************/
String& String::operator+(const char* pChar) //Overloaded operator + (Adds char
{                                            //literals or pointers to Asciiz
int iLen,iNewSize;                          //Strings)
char* pNew;

iLen=strlen(this->pStrBuffer)+strlen(pChar);
if(iLen<this->iAllowableCharacterCount)     //Can we accomodate both Strings???
{
    if(this->pStrBuffer)  //Something is already stored in this...
       strcat(this->pStrBuffer,pChar);       //So Concatenate
    else                  //Its a NULL String
       strcpy(this->pStrBuffer, pChar);      //So just copy to pStrBuffer
}
else  //Can't accomodate both Strings, so a new memory allocation is needed.
{
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount = iNewSize-1;
    if(this->pStrBuffer)
    {
       strcpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       strcat(pNew,pChar);
    }
    else
       strcpy(pNew,pChar);
    this->pStrBuffer=pNew;
}

return *this;
}


/* ****************************************************************************/
// String& String::operator+(const String& strRight)  //adds another String
//
// This overloaded operator+ will be called by a construction such as this...
//
// String s1 = "John ";
// String s2 = "Jones";
// s1 = s1 + s2;
//
// which is equivalent to...
//
// s1.operator+(s2);
//
// The 1st thing the function does is find out the total length of the existing
// String plus the length of the String to be added - the parameter strRight.
// If this total length, i.e., iLen, is less than this->iAllowableCharacterCount
// then the code must next determine whether or not anything is already stored
// at this->pStrBuffer.  If pStrBuffer points to a NULL String the parameter
// pChar is simply copied to there.  If something is at pStrBuffer then a
// concatenation ( strcat() ) is in order.  The same basic logic is followed if
// the combined length of both Strings is such that a new and larger memory
// allocation is needed (see the else at mid point).
/* ****************************************************************************/
String& String::operator+(const String& strRight)  //Overloaded operator + Adds
{                                                  //Another String to the left
int iLen,iNewSize;                                //operand.
char* pNew;

printf("\nEntering operator+(const String& strRight)\n");
printf("  strRight.pStrBuffer = %s\n", strRight.pStrBuffer);
iLen=strlen(this->pStrBuffer) + strlen(strRight.pStrBuffer);
if(iLen < this->iAllowableCharacterCount)
{
    if(this->pStrBuffer)
       strcat(this->pStrBuffer,strRight.pStrBuffer);
    else
       strcpy(this->pStrBuffer,strRight.pStrBuffer);
}
else
{
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new char[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    if(this->pStrBuffer)
    {
       strcpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       strcat(pNew,strRight.pStrBuffer);
    }
    else
       strcpy(pNew,strRight.pStrBuffer);
    this->pStrBuffer=pNew;
}
printf("Leaving operator+(const String& strRight)\n\n");

return *this;
}

/* ************************************************************************** */
// int String::LenStr(void) // Simply return String Length ( strlen() ) of the
// private data member this->pStrBuffer.
/* ************************************************************************** */
int String::LenStr(void)
{
return strlen(this->pStrBuffer);
}

/* ************************************************************************** */
// char* String::lpStr()  // Simply return the address of private data member
// this->pStrBuffer.  This could be needed for example in printf.
/* ************************************************************************** */
char* String::lpStr()
{
return pStrBuffer;
}


/* ************************************************************************** */
// This is the String Destructor.  It is called when a String is being
// destroyed.  In such cases it is considered good practice to set any class
// buffers to 0 so the same address does't accidentally get deleted twice.
// Calling delete on a NULL pointer is OK; calling it twice on a non null
// pointer isn't OK, because there may be something else at that address.
/* ************************************************************************** */
String::~String()   //String Destructor
{
printf("\nEntering String Destructor\n");
printf("  pStrBuffer = %u\n",(unsigned int)pStrBuffer);
delete [] pStrBuffer;
pStrBuffer=0;
printf("Leaving String Destructor\n\n");
}