Practical C++ Programming
Authors: Oualline S.
Published year: 2003
Pages: 270-272/364
Buy this book on amazon.com >>
I l @ ve RuBoard

25.3 Creating a Waiting List with the STL List

Suppose we have more students who want to take a class than we have places to put them. In that case we'll have to start a waiting list for those people who want to get in if space opens up.

We can't use a set as a waiting list, because a set is unordered and we want to make our list first come, first served . For that we need the STL container std::list . A std::list is an ordered list of elements that allows us to add students on the back end and remove them from the front end.

Our waiting list declaration is:

#include <list>

std::list<std::string> waiting_list; // Waiting list for the class

When we want to add a student to the back of the list, we use the code:

waiting_list.push_back(student);

To remove a student from the front, we need two statements:

student = waiting_list.top(  );
    waiting_list.pop_front(  );

Iterating through a list is just like iterating through a set . That's because they are both containers, and the STL is designed so that all containers act the same whenever possible.

I l @ ve RuBoard
I l @ ve RuBoard

25.4 Storing Grades in a STL Map

Let's change our class roster so that we record not only the name of each student, but also the student's grade as well. We do this using something called a map . To define our class map, we use the following declaration:

#include <map>

// Map key=name(string), value = grade(char)
template map<string, char> student_roster;

Inserting an item into a map is a little trickier that inserting one into a set because we are inserting a pair of items. The STL handles this nicely by providing a pair class that takes two elements and turns them into an item that a map can handle. For example, if John Smith is in the class and got an A, we would write:

student_roster(pair(string("John Smith"), 'A'));

Suppose we want to find out what Mr. Smith's grade is. We can search for his record using the find function call. It takes three parameters: a place to start the search, a place to end the search, and something to look for. It returns an iterator pointing to the item found or, if nothing is found, the value returned by the end function. To look for Mr. Smith's grade, we use the code:

map<string, char>::const_iterator record_loc;

    record_loc = find(student_roster.begin(), student_roster.end(  );
                    string("John Smith"));

Now let's check to see if we found the student:

if (record_loc == student_roster.end(  ))
        std::cerr << "John Smith not found in the class\n";

The iterator points to a pair consisting of the student's name and grade. The fields of a pair are named first and second . To print John's record, we use the statement:

std::cout << "Student: " << record_loc->first << " Grade:" << 
        record_loc->second << '\n';
I l @ ve RuBoard
I l @ ve RuBoard

25.5 Putting It All Together

Now let's use our knowledge of the STL to create a class to handle a simple class roster and waiting list. The class class_stuff will contain member functions to perform the following tasks :

  • Add a student to a class. If the class is full, the student will be added to the waiting list.

  • Remove a student from a class. If the waiting list has a student in it, the first student on the waiting list gets put in the class.

  • Record a grade for each assignment.

  • Print a class roster including grades.

Let's start by defining the class. We'll record information about students in the class in a map . The key is the student's name , and the value is a vector containing the grades.

A vector is very similar to a list . However, it allows indexed access to its elements much the same way an array does. We will use the function size to determine the number of elements in the vector and resize to increase the size if needed.

The start of our class_stuff definition looks like this:

class class_stuff {
    public:     
        typedef std::vector<int> grades;        // A set of grades

        std::map<std::string, grades> roster;  // Roster of current class
        std::list<std::string> waiting_list;   // People waiting on the list

Now we need to define the member functions. The first one adds a student to a class:

void class_stuff::add_student(
    const string& name  // Name of the student to add
)
{

We first check to see if the student is already in the class; if she is, we don't add her again:

if (roster.find(name) != roster.end(  ))
        return; // Already in the class, don't reuse

Next we check to see if the number of students currently in the class has reached the limit. If there is room, we add the student to the class using the new_student function described below. If there is not room in the class, the student goes on the end of the waiting list:

if (roster.size(  ) < MAX_STUDENTS) {
        // Class has room, add to class
        new_student(name);
    } else {
        // No room, put on waiting list
        waiting_list.push_back(name);
    }
}

The new_student function is responsible for adding a student to the roster. It creates an empty set of grades, then inserts the student into the class:

// Insert a student into the class
        void class_stuff::new_student(
            const string& name  // Student to add to the class
        )
        {
            grades no_grades;   // Empty grade vector
            roster.insert(pair<string, grades>(name, no_grades));
        }
};

The code to drop a student first checks to see if the student is actually in the class; he can't be dropped if he's not enrolled:

void class_stuff::drop_student(
    const string& name  // Name of the student to drop
)
{
    // The student we are probably going to drop
    map<string, grades>::iterator the_student =
        roster.find(name);

    if (the_student == roster.end(  ))
        return; // Student is not in the class

Next we remove the student from the class. The erase member function eliminates an element from the map:

roster.erase(name);

Finally we check the waiting list and add one student from it if there's someone available:

// Add a person from the waiting_list if 
    // there's anyone waiting
    if (waiting_list.size(  ) > 0) {
        string wait_name = waiting_list.front(  );
        waiting_list.pop_front(  );
        new_student(wait_name);
    }
}

To record a grade, we first find the student's record:

void class_stuff::record_grade(
    const string& name,         // Name of the student
    const int grade,            // Grade of this assignment
                                // Assignment number
    const unsigned int assignment_number
)
{
    map<string, grades>::iterator the_student =
        roster.find(name);

    if (the_student == roster.end(  ))
    {
        std::cerr << "ERROR: No such student " << name << '\n';
        return;
    }

Then we adjust the size of the grade vector so that it has enough entries to contain the assignment:

// Resize the grade list if there's not enough room
    if (the_student->second.size(  ) <= assignment_number)
        the_student->second.resize(assignment_number+1);

Finally, we store the value of the grade:

the_student->second[assignment_number] = grade;
}

Our last function prints the grades for all the students in a class. To do this we need a sorted list of names. We start by copying the names from the roster into a container ( storted_names ) that can be sorted:

void class_stuff::print_grades(  )
{
    std::vector<std::string> sorted_names;        // Student names sorted

    // The student we are inserting into the storted_names list
    map<string, grades>::iterator cur_student;

    for (cur_student = roster.begin(  );
         cur_student != roster.end(  );
         ++cur_student)
    {
        sorted_names.push_back(cur_student->first);
    }

The std:: sort function is one of the algorithms supplied by the STL. You give it a range of items to sort and it sorts them. In this case, we want to sort the entire std::vector from beginning to end:

sort(sorted_names.begin(  ), sorted_names.end(  ));

Now it's simply a matter of stepping through the sorted name list and printing the data. About the only new thing in this section of code is the use of [] to access elements of the roster . Remember that roster is a mapping of key to value. One way to find out the value associated with a particular key is to use the [] as follows :



value


=


a_map


[


key


];

The rest of the code for printing the grades is pretty straightforward:

// The current student to print
    std::vector<std::string>::const_iterator cur_print;

    for (cur_print = sorted_names.begin(  );
         cur_print != sorted_names.end(  );
         ++cur_print)
    {
        std::cout << *cur_print << '\t';

        // The grade we are printing now
        grades::const_iterator cur_grade;

        for (cur_grade = roster[*cur_print].begin(  );
             cur_grade != roster[*cur_print].end(  );
             ++cur_grade)
        {
            std::cout << *cur_grade << ' ';
        }
        std::cout << '\n';
    }
}

Example 25-1 shows the full code for our class_stuff object, as well as some test code.

Example 25-1. class/class.cpp
/********************************************************
 * class_stuff -- A simple class to handle students     *
 * and grades.                                          *
 ********************************************************/
#include <iostream>

#include <string>
#include <vector>
#include <map>
#include <list>

#include <algorithm>

const unsigned int MAX_STUDENTS = 5;    // Max number of students per class
// Set low for testing

class class_stuff {
    public:     
        typedef std::vector<int> grades;// A set of grades

        std::map<std::string, grades> roster;   // Roster of current class
        std::list<std::string> waiting_list;    // People waiting on the list
    public:
        // Constructor defaults
        // Destructor defaults
        // Copy constructor defaults
        // Assignment operator
    public:
        void add_student(const std::string& name);
        void drop_student(const std::string& name);
        void record_grade(const std::string& name, 
                const int grade,
                const unsigned int assignment_number
        );
        void print_grades(  );
    private:
        // Insert a student into the class
        void new_student(
            const std::string& name     // Student to add to the class
        )
        {
            grades no_grades;   // Empty grade vector
            roster.insert(
                std::pair<std::string, grades>(name, no_grades));
        }
};

/********************************************************
 * class_stuff::add_student -- Add a student to a class *
 *      If the class if full, add him to the waiting    *
 *      list.                                           *
 *********************************************************/
void class_stuff::add_student(
    const std::string& name     // Name of the student to add
)
{
    if (roster.find(name) != roster.end(  ))
        return; // Already in the class, don't reuse

    if (roster.size(  ) < MAX_STUDENTS) {
        // Class has room, add to class
        new_student(name);
    } else {
        // No room, put on waiting list
        waiting_list.push_back(name);
    }
}
/********************************************************
 * class_stuff::drop_student -- Remove student from     *
 * a class.  If there's a waiting list his place is     *
 * filled by the first student on the list.             *
 ********************************************************/
void class_stuff::drop_student(
    const std::string& name     // Name of the student to drop
)
{
    // The student we are probably going to drop
    std::map<std::string, grades>::iterator the_student =
        roster.find(name);

    if (the_student == roster.end(  ))
        return; // Student is not in the class

    roster.erase(name);
    // Add a person from the waiting_list if 
    // there's anyone waiting
    if (waiting_list.size(  ) > 0) {
        std::string wait_name = waiting_list.front(  );
        waiting_list.pop_front(  );
        new_student(wait_name);
    }
}

/********************************************************
 * class_stuff::record_grade -- Record a grade for      *
 *      a student.                                      *
 ********************************************************/
void class_stuff::record_grade(
    const std::string& name,    // Name of the student
    const int grade,            // Grade of this assignment
                                // Assignment number
    const unsigned int assignment_number
)
{
    std::map<std::string, grades>::iterator the_student =
        roster.find(name);

    if (the_student == roster.end(  ))
    {
        std::cerr << "ERROR: No such student " << name << '\n';
        return;
    }
    // Resize the grade list if there's not enough room
    if (the_student->second.size(  ) <= assignment_number)
        the_student->second.resize(assignment_number+1);

    the_student->second[assignment_number] = grade;
}

/********************************************************
 * class_stuff::print_grades -- Print the students      *
 * and their grades.                                    *
 ********************************************************/
void class_stuff::print_grades(  )
{
    std::vector<std::string> sorted_names;      // Student names sorted

    // The student we are inserting into the storted_names list
    std::map<std::string, grades>::iterator cur_student;

    for (cur_student = roster.begin(  );
         cur_student != roster.end(  );
         ++cur_student)
    {
        sorted_names.push_back(cur_student->first);
    }
    std::sort(sorted_names.begin(  ), sorted_names.end(  ));

    // The current student to print
    std::vector<std::string>::const_iterator cur_print;

    for (cur_print = sorted_names.begin(  );
         cur_print != sorted_names.end(  );
         ++cur_print)
    {
        std::cout << *cur_print << '\t';

        // The grade we are printing now
        grades::const_iterator cur_grade;

        for (cur_grade = roster[*cur_print].begin(  );
             cur_grade != roster[*cur_print].end(  );
             ++cur_grade)
        {
            std::cout << *cur_grade << ' ';
        }
        std::cout << '\n';
    }
}


int main(  )
{
    // A class for testing
    class_stuff test_class;

    test_class.add_student("Able, Sam");
    test_class.add_student("Baker, Mary");
    test_class.add_student("Johnson, Robin");
    test_class.add_student("Smith, Joe");
    test_class.add_student("Mouse, Micky");

    test_class.add_student("Gadot, Waiting");
    test_class.add_student("Congreve, William");

    std::cout << "Before drop " << std::endl;
    test_class.print_grades(  );
    std::cout << "\n";
    
    test_class.drop_student("Johnson, Robin");

    std::cout << "After drop " << std::endl;
    test_class.print_grades(  );
    std::cout << "\n";

    int i;

    for (i = 0; i < 5; ++i)
    {
        test_class.record_grade("Able, Sam",      i*10+50, i);
        test_class.record_grade("Baker, Mary",    i*10+50, i);
        test_class.record_grade("Smith, Joe",     i*10+50, i);
        test_class.record_grade("Mouse, Micky",   i*10+50, i);
        test_class.record_grade("Gadot, Waiting", i*10+50, i);
    }

    std::cout << "Final " << std::endl;
    test_class.print_grades(  );
    std::cout << "\n";

    return (0);
}
I l @ ve RuBoard
Practical C++ Programming
Authors: Oualline S.
Published year: 2003
Pages: 270-272/364
Buy this book on amazon.com >>

Similar books on Amazon