Special purpose containers

The containers described so far are flexible and can be used for all kinds of purposes. The Standard Library provides classes that have specific purposes, but, because they are implemented by wrapping other classes, they are called container adapters. For example, a deque object can be used as a first-in first-out (FIFO) queue, by pushing objects to the back of the deque (with push_back) and then accessing objects from the front of the queue using the front method (and removing them with pop_front). The Standard Library implements a container adapter called queue that has this FIFO behavior, and it is based on the deque class.

    queue<int> primes; 
primes.push(1);
primes.push(2);
primes.push(3);
primes.push(5);
primes.push(7);
primes.push(11);
while (primes.size() > 0)
{
cout << primes.front() << ",";
primes.pop();
}
cout << endl; // prints 1,2,3,5,7,11

You push items into the queue and remove them with pop, and the next item is accessed using the front method. The Standard Library containers that can be wrapped by this adapter must implement the push_back, pop_front, and front methods. That is, items are put into the container at one end and accessed (and removed) from the other end.

A last-in first-out (LIFO) container will put in items and access (and remove) items from the same end. Again, a deque object can be used to implement this behavior by pushing items using push_back, accessing the items using front, and removing them with the pop_back method. The Standard Library provides an adapter class called stack to provide this behavior. This has a method called push to push items into the container, a method called pop to remove items, but, oddly, you access the next item using the top method, even though it is implemented using the back method of the wrapped container.

The adapter class priority_queue, in spite of the name, is used like the stack container; that is, items are accessed using the top method. The container ensures that when an item is pushed in, the top of the queue will always be the item with the highest priority. A predicate (the default is <) is used to order the items in the queue. For example, we could have an aggregate type that has the name of a task and the priority in which you must complete the task compared to other tasks:

    struct task 
{
string name;
int priority;
task(const string& n, int p) : name(n), priority(p) {}
bool operator <(const task& rhs) const {
return this->priority < rhs.priority;
}
};

The aggregate type is straightforward; it has two data members that are initialized by the constructor. So that tasks can be ordered, we need to be able to compare two task objects. One option (given earlier) is to define a separate predicate class. In this example, we use the default predicate, which the documentation says will be less<task>, and this compares items based on the < operator. So that we can use the default predicate, we define the < operator for the task class. Now we can add tasks to a priority_queue container:

    priority_queue<task> to_do; 
to_do.push(task("tidy desk", 1));
to_do.push(task("check in code", 10));
to_do.push(task("write spec", 8));
to_do.push(task("strategy meeting", 8));

while (to_do.size() > 0)
{
cout << to_do.top().name << " " << to_do.top().priority << endl;
to_do.pop();
}

The result of this code is:

    check in code 10
write spec 8
strategy meeting 8
tidy desk 1

The queue has ordered the tasks according to the priority data item, and the combination of top and pop method calls reads the items in priority order and removes them from the queue. Items with the same priority are placed in the queue in the order in which they were pushed in.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset