C-strings:

    Definition:  a character array whose useful data ends in a
                 'null' character ('\0' -- ASCII 0).

    Since ASCII 0 cannot be produced from the keyboard, it cannot
    be part of true user data.  Therefore we use it to mark the
    end of a string's useful data.

    This means, of course, that we must make a string array 1 element
    longer than the data we intend it to hold (to store the null-
    character).

    On the positive side, we no longer have to pass the length of
    the array to all string functions (as we do with other arrays).
    We can also output a string on cout without any extra loops to
    print each element one at a time.

    We can even input a string with cin without a loop.  However,
    we must tell cin about the length of the array so that overflow
    won't occur.  We do this in one of two ways.  For space-separated
    input (where our string doesn't need to contain spaces), you can
    use the setw() manipulator (iomanip library):

        cin >> setw(MAX) >> str;

    To read a string (str) of a specified length (MAX).  cin will
    automatically adjust for the null-character (reading only MAX-1
    characters or until it finds a space character).

    Reading space-inclusive data (a string which CAN include spaces)
    is done with the getline method of cin's class:

        cin.getline(str, MAX);

    Similarly to the setw(), cin will stop when it reads MAX-1 characters.
    However, now it will read spaces into the string -- except for Enter's.
    It will remove '\n' characters from the stream, but not place them
    into the string.

    Unfortunately, when mixing getline and >>, the '\n' characters left
    behind by >> will often confuse getline into reading empty strings.
    To avoid this, simply use ignore (possibly in conjunction with a
    peek).  (See the input library for an example.)

    Although many common operations don't work with strings (=, <=, >=,
    ==, !=, <, >, +=, +), we've been given a library (string.h is its
    header) of functions to replace these operations:

        s1 = s2   -->  strcpy(s1, s2)

        s1 += s2  -->  strcat(s1, s2)

        s1 < s2   -->  strcmp(s1, s2) < 0
        s1 <= s2  -->  strcmp(s1, s2) <= 0
        s1 > s2   -->  strcmp(s1, s2) > 0
        s1 >= s2  -->  strcmp(s1, s2) >= 0
        s1 == s2  -->  strcmp(s1, s2) == 0
        s1 != s2  -->  strcmp(s1, s2) != 0

    Of course none of these receive the strings' allocated lengths and
    so you must take care to ensure there is enough room for copying
    and concatenation.  There are also 'n' versions of each which allow
    you to tell the function how many characters to process at most:

        strncpy(s1, s2, n)  // copy at most n characters from s2 to s1
        strncat(s1, s2, n)  // concatenate at most n characters from s2 to s1
        strncmp(s1, s2, n)  // compare at most n characters from s2 to s1

    However, the 'n' versions are NOT guaranteed to copy/concatenate
    the null-character onto the destination string (s1).  Therefore
    you should follow these with an assignment such as:

        strncpy(s1, s2, MAX-1);
        s1[MAX-1] = '\0';

    (This could be MAX-2 in the assignment, but it is easier to remember
    if they are the same.)  (See the set_name method in the Person class.)

    Finally remember that, since we don't pass the length of a string
    to string processing functions, we don't know how long the string
    is.  You could call strlen to find the length of the string and
    then use a for loop to process those elements.  However, in order
    for strlen to know the length of the string, it must process all
    of the elements looking for the '\0' at the end.  So, doing this
    for loop method we will process all the string's elements twice:
    once for strlen and again in our for loop.

    To avoid this, we typically process strings with an indefinite
    loop such as:

        i = 0;
        while (str[i] != '\0')
        {
            // do something to str[i]
            i++;
        }

    This way we stop when we are out of data (reaching the '\0' character)
    and only process each element once (rather than twice when using a
    strlen-bounded for loop).

Libraries:

    A library consists (typically) of an interface/header file and an
    implementation file.  The interface (.h) file will contain any
    constants, type definitions (including class definitions), and
    prototypes which are to be public/available to users of the library.
    The implementation (.C) file will contain definitions of those
    functions prototyped in the interface file.  (Note that if a
    function is inline, it cannot be prototyped and therefore its
    definition must go in the header file!)  Both files will have
    the same name -- just different extensions.

    The header file should also contain special preprocessor directives
    to help avoid circular inclusion of libraries.  Choose a unique
    symbol for each library (often the library name with _H_INC appended
    in all capital letters).  Then place the following lines before
    your header file:

        #ifndef SYMBOL
        #define SYMBOL

    and this line after your header file:

        #endif

    This will avoid having header files include each other in an
    infinite cycle.

Libraries and Namespaces:

    It is fine for the library's implementation (.C) file to have a
    using statement since it is a separate source file that won't be
    #include'd into any other file.  The interface (.h) file, on the
    other hand, should *NEVER* have a using statement.  Were an inter-
    face file to have a using statement, it would force all files that
    #include it to use the same namespace(s) it does.  That's just not
    neighborly.  Instead, you must preface all standard library symbols
    used in the interface file with 'std::' to indicate that they are
    from inside the std namespace.  Note on the input.h file:

        std::cout ...
        std::cin ...
        return std::toupper...

Stream Processing:

    Note how the peek and ignore methods are used in the get_line
    function to avoid having a blank line read as legitimate data.

    Also note how we had to call cout's flush method to ensure that
    any pending prompting was printed on the screen.  This is merely
    because HP/UX's library (and probably other library versions as
    well) fails to flush the screen when the keyboard is peek'ed.
