INSERTING IN SEQUENCES ■ 759 ᮀ Insertion Methods The following methods are defined in the container classes vector, deque, and list ■ push_back() insert at end ■ insert() insert after a given position. Additionally, the following method is available in the list and deque classes ■ push_front() insert at beginning. This method is not defined in the vector class. The insert() method is overloaded in various versions, allowing you to insert a sin- gle object, multiple copies of an object, or object copies from another container. Given two containers v and w the following Example: w.insert( w.begin(), v.begin(), v.end()); inserts the objects from container v in front of all the other objects in w. A container can of course be assigned to another container of the same type. The assignment operator is overloaded for containers to allow this operation. ᮀ Runtime Behavior The push_back() and push_front() methods are preferable on account of their constant runtime. Insertion of one object with the insert() method also has a con- stant runtime in the list class. However, this is linear in the vector and deque classes, that is, the time increases proportionally to the number of objects in the con- tainer. This dissimilar runtime behavior for methods can be ascribed to the implementation of various container classes. Normally, containers of the list type are represented by double linked lists in which each element possesses a pointer to the preceding and fol- lowing element. This allows for extremely quick inserting at a given position. The container classes vector and deque are represented as arrays. Inserting in the middle means shifting the objects in the container to make place for the new object. Therefore the runtime will increase proportionally with the number of objects the con- tainer holds. ᮀ Insertion in Adapter Classes There is only one insertion method for adapter classes: push(). In stacks and queues, push() appends an object with a constant runtime. Insertion of objects into priority queues depends on the priority of the object and the runtime is linear. 760 ■ CHAPTER 33 CONTAINERS // sortvec.h // Method search() seeks an object obj in the vector // using the binary search algorithms: // The object obj is first compared to the element in // the middle of the vector. If obj is smaller than the // "middle" element, it must belong to the left half or // else to the right half of the vector. We repeat this // process comparing obj with the "middle" element in // the section where it is known to be, repeatedly // halving the size of the interval until the interval // consists of a single point, which is where obj // belongs. // This algorithm has logarithmic time and thus // is very fast. // template <class T, class Compare > int SortVec<T, Compare>::search(const T& obj) { int first = 0, last = end() - begin() - 1, mid; while( first < last ) { mid = (first + last + 1)/ 2; // Search the left half, if( obj < (*this)[mid] ) last = mid - 1; else first = mid; // the right half. } if ( obj == (*this)[first] ) // Found? return first; // Yes. else return size(); // Not found. } ■ ACCESSING OBJECTS Method search() of container class sortVec ACCESSING OBJECTS ■ 761 ᮀ The front() and back() Methods Access to individual objects in the container classes vector, deque, and list can be performed by the following methods ■ front() for access to the first element and ■ back() for access to the last element. Both methods return a reference to the object in question. Example: double z = v.front(); v.front() = 1.9; This saves the first object in container v in the variable z and then overwrites the object by 1.9. ᮀ Access via Indices The subscript operator [] is overloaded in the vector and deque classes to permit the use of indices for access to the objects in a container. An index is a positive integer of the type size_type. Example: v[20] = 11.2; // the 21st object Given that pos is an iterator that references the first object in a container v, the expres- sion v[20] is equivalent to pos[20]. When you use the subscript operator, you must ensure that the index does not exceed the valid range. You can use the access method at() to throw an exception if an index is out of range. Example: v.at(20) = 11.2; The at() method throws an exception of the standard error class out_of_range if an error occurs. The subscript operator and the at() method are not defined in the list class. Before you can manipulate the tenth object in the container, for example, you need to walk through the container sequentially up to that position. ᮀ Access to Objects in Adapter Classes The method top() is defined for access to the element with the highest priority, or the element at the top of the stack, in the adapter classes priority_queue and stack. The queue class comprises the front() method, which is used to access the first element. 762 ■ CHAPTER 33 CONTAINERS // sortvec.h: Method merge() merges the argument vector // with the vector *this. // template <class T, class Compare > void SortVec<T,Compare>::merge(const SortVec<T, Compare>& v) { SortVec temp; // Temporary vector SortVec::iterator pos = begin(); // Iterator int n1 = 0, n2 = 0; // Copy the smaller object into vector temp: while(n1 < size() && n2 < v.size()) if( pos[n1] <= v[n2] ) temp.push_back(pos[n1++]); else temp.push_back(v[n2++]); // Append the rest: while( n1 < size()) temp.push_back(pos[n1++]); while( n2 < v.size()) temp.push_back(v[n2++]); *this = temp; } ■ LENGTH AND CAPACITY Method merge() of container class SortVec LENGTH AND CAPACITY ■ 763 The identifying features of a container are ■ its length, that is, the number of objects held in the container, and ■ the capacity, that is, the maximum number of objects the container can store. The length of a container changes after every insertion or deletion—the capacity does not. ᮀ Length of a Container The length of a container is discovered by a call to the size() method. The method returns an integer of the size_type type. Example: Fraction x(1, 1); vector<Fraction> v(100, x); vector<Fraction>::size_type sz = v.size(); The variable sz contains the value 100 in this case. The length of an empty container is always 0. You can also use the empty() method to discover whether a container is empty. The method returns true in this case. Example: while( !cont.empty() ) The methods size() and empty() are defined for all container classes. You can use the resize() method to change the length of a container. Example: cont.resize( n, x); The length is increased to n provided n > size() is true, or decreased for n < size() . If n == size(), nothing happens. If the length is increased, n – size() copies of the object x are appended to the container. The second argument, x, can be omitted. In this case, the default constructor for a type T object is called as often as necessary. ᮀ Capacity The capacity of a container can be checked using the max_size() method. Example: size_type k = cont.max_size(); The return value depends on the amount of memory available and the object size. Only the size() and empty() methods are defined for adapter classes. You cannot discover the capacity of an object, nor can you call resize() to change its length. 764 ■ CHAPTER 33 CONTAINERS // prior_t.cpp : Testing a priority queue // #include <queue> #include <string> #include <iostream> using namespace std; class Parcel { private: unsigned int prio; // Priority string info; public: Parcel(unsigned int p, const string& s) :prio(p), info(s) {} // Access methods, overloaded operators: friend bool operator<(const Parcel& x,const Parcel& y) { return (x.prio < y.prio); } friend ostream& operator<<(ostream& os, const Parcel& x) { os << x.prio << " "<< x.info << endl; return os; } }; int main() { priority_queue<Parcel> pq; pq.push(Parcel(7,"Bob")); // Insert pq.push(Parcel(1,"Peter")); pq.push(Parcel(4,"Susan")); while( !pq.empty() ) { cout << pq.top() << endl; // Output pq.pop(); // and delete } return 0; } // Output: 7 Bob // 4 Susan // 1 Peter ■ DELETING IN SEQUENCES A priority queue DELETING IN SEQUENCES ■ 765 ᮀ Deletion Methods The following methods are available for deleting objects in the container classes vec- tor , deque, and list: ■ pop_back() deletes the last object in the container ■ erase() deletes the object at a given position, or deletes all the objects in a given range ■ clear() deletes all the objects in a container. The following method is additionally defined in the deque and list classes: ■ pop_front() deletes the first object in the container. The method does not have a return value, just like the pop_back() method. The pop_back() and pop_front() methods are preferable on account of their constant runtimes. Using the erase() method to delete an object at the beginning or in the middle of a container also provides a constant runtime in the container class list. However, the runtime is linear in the vector and deque classes, since objects must be shifted within the container to fill the gap left by the deletion. ᮀ Deleting Ranges of Objects When you use the erase() method to delete the objects in a given range, the position of the first element to be deleted and the position after the last object to be deleted are required as arguments. Example: cont.erase(cont.begin() + 10, cont.end()); This deletes all the remaining objects in the container, starting at position 11. The erase() method returns the new position of the object immediately after the range of objects deleted. ᮀ Deletion in Adapter Classes There is only one method of deletion for adapter classes, namely pop(). Given that wait is a queue of the queue type, the following statement Example: wait.pop(); deletes the element at the beginning of the queue. In the case of a stack, pop() deletes the element at the top of the stack, and for priority queues, the object with the highest priority. The runtime is constant in all cases. 766 ■ CHAPTER 33 CONTAINERS // list_t.cpp: Tests list operations // #include <list> #include <cstdlib> #include <iostream> using namespace std; typedef list<int> INTLIST; int display( const INTLIST& c); int main() { INTLIST ls, sls; int i; for( i = 1; i <= 3; i++) ls.push_back( rand()%10 ); // ex. 1 7 4 ls.push_back(ls.front()); // 1 7 4 1 ls.reverse(); // 1 4 7 1 ls.sort(); // 1 1 4 7 for( i = 1; i <= 3; i++) sls.push_back( rand()%10 ); // ex. 0 9 4 // Insert first object of sls before the last in ls: INTLIST::iterator pos = ls.end(); ls.splice( pos, sls, sls.begin()); // 1 1 4 0 7 display(sls); // 9 4 ls.sort(); // 0 1 1 4 7 sls.sort(); // 4 9 ls.merge(sls); // 0 1 1 4 4 7 9 ls.unique(); // 0 1 4 7 9 return 0; } ■ LIST OPERATIONS Sample program LIST OPERATIONS ■ 767 The container class list comprises methods for list operations that are not defined in other container classes. These are ■ sorting and inverting lists ■ merging two sorted lists ■ splicing lists. ᮀ Sorting, Inverting, and Splicing Lists A container of the list type, or list container for short, can be sorted by a call to sort(). This assumes that the operator < is defined in class T. A call to sort() sorts the container in ascending order. You can use the reverse() method to invert a list container, that is, to reverse the order of the objects in the container. What was originally the first element in the con- tainer becomes the last, and the second element becomes the second to last, and so on. The merge() method is used to merge two list containers. Given that ls1 and ls2 are two sorted list containers, the following call Example: ls1.merge(ls2); creates the sorted list ls1, whose objects comprise the original objects of ls1 and ls2. The ls2 container is empty following this operation. ᮀ Splice Operations Splice operations insert the objects from one list container at a given position in another list container and remove them from the original container. You can transfer either a whole container or just part of a container. Example: ls1.splice(pos, ls2); This inserts the whole of container ls2 in front of position pos in ls1. ls2 is emptied by this statement. The following statement Example: ls1.splice(pos1, ls2, pos2); Inserts the element at position pos2 in ls2 before the element at position pos1 in ls1 and deletes it from ls2. If you want to transfer part of a container, the third and fourth arguments must contain the starting and end position. You cannot use a splice operation to insert at a position before begin() or after end(). 768 ■ CHAPTER 33 CONTAINERS Container Class Representing set< class T, class Compare = less<T>, class Allocator = allocator<T> > collections of objects with unique keys multiset< class T, class Compare = less<T>, class Allocator = allocator<T> > collections of objects with equivalent keys, i.e. possibly multiple copies of the same key value map< class Key, class T, class Compare = less<T>, class Allocator = allocator<T> > collections of objects/key pairs where the keys are unique multimap< class Key, class T, class Compare = less<T>, class Allocator = allocator<T> > collections of objects/key pairs with possibly equivalent keys Container Class Header File set< T, Compare, Allocator > multiset<T, Compare, Allocator > map< Key, T, Compare, Allocator > multimap< Key, T, Compare, Allocator > <set> <set> <map> <map> ■ ASSOCIATIVE CONTAINERS Container classes Associative containers and header files . want to transfer part of a container, the third and fourth arguments must contain the starting and end position. You cannot use a splice operation to insert at a position before begin() or after end(). 768 ■ CHAPTER. Allocator > map< Key, T, Compare, Allocator > multimap< Key, T, Compare, Allocator > <set> <set> <map> <map> ■ ASSOCIATIVE CONTAINERS Container classes Associative. assumes that the operator < is defined in class T. A call to sort() sorts the container in ascending order. You can use the reverse() method to invert a list container, that is, to reverse the order