sequential containers based on koffmann and wolfgang chapter 4
Post on 20-Dec-2015
222 views
TRANSCRIPT
Chapter 4: Sequential Containers 2
Chapter Outline
• Template Classes and the Vector• Applications of vector• Implementation of the vector class• The Copy Constructor, Assignment Operator, and
Destructor• Single-Linked Lists and Double-Linked Lists• The list class and the Iterator• Implementation of a Double-Linked List Class• Applications of the list Class• Standard Library Containers• Standard Library Algorithms and Function Objects
Chapter 4: Sequential Containers 3
Template Container Classes
• A template container class is a class that stores and processes a collection of information.
• The type of this information is a parameter that is specified when the template class is instantiated.
• Example:template<typename T>class some_container { … }
• The parameter T is a placeholder for the actual data type.
some_container<Item_Type> call_lengths; // holds Item_Type
Some_container<People> people; // holds People
Chapter 4: Sequential Containers 4
The Vector
• An enhancement of the array.• Can be used like an array:
• Access a value via an indexx = v[i];
v[j] = z;
• Additional capabilities:• Increase or decrease its length• Insert an element at a specified position• Remove an element from a specified position
Chapter 4: Sequential Containers 5
Vector Example
// Create a vector to hold strings.vector<string> my_vector;// Add some entries.my_vector.push_back("Bashful");my_vector.push_back("Awful");my_vector.push_back("Jumpy");my_vector.push_back("Happy");
Chapter 4: Sequential Containers 6
Vector Example (2)
Had to slide [2],[3] to [3],[4]
my_vector.insert(2, “Doc”);
Chapter 4: Sequential Containers 7
Vector Example (3)
Cheap to add at end
my_vector.push_back("Dopey"); // add at end
Chapter 4: Sequential Containers 8
Vector Example (4)
Had to slide [2..5] to [1..4]
Replacement is cheap
my_vector.erase(1);
lst.set(1, “Sneezy”);
Chapter 4: Sequential Containers 9
Example Applications of vector
vector<Item_Type> some_Item_Types;Item_Type nums[] = {5, 7, 2, 15};for (size_t i = 0; i < 4; i++) some_Item_Types.push_back(nums[i]);
Item_Type sum = 0;for (size_t i = 0; i < 4; i++) { sum += some_Item_Types[i];}cout << "sum is " << sum << endl;
Chapter 4: Sequential Containers 10
Using vector in PhoneDirectory
vector<Directory_Entry> the_directory;
/** Adds a new entry @param name The name of the person @param new_number The new number*/void add(string name, string number) { Directory_Entry new_entry(name, number); the_directory.push_back(new_entry);}
Chapter 4: Sequential Containers 11
Using vector in PhoneDirectory (2)string Phone_Directory::add_or_change_entry(const string& name,
const string& number)
{ string old_number = ""; Item_Type index = find(name); if (index != -1) { old_number = the_directory[index].get_number(); the_directory[index].set_number(number); } else { add(name, number); } modified = true; return old_number;}
Chapter 4: Sequential Containers 12
Implementing a vector Class
• KW::vector: simple implementation of std::vector• Physical size of array indicated by data field current_capacity
• Number of data items indicated by the data field num_items
Chapter 4: Sequential Containers 13
KW::vector Fields, Constructor template<typename Item_Type> class vector { private: // Data fields /** The initial capacity of the array */ static const size_t INITIAL_CAPACITY = 10; /** The current capacity of the array */ size_t current_capacity; /** The current num_items of the array */ size_t num_items; /** The array to contain the data */ Item_Type* the_data; public: // Member Functions /** Constructs<i> </i>an empty vector with the default
initial capacity. */ vector<Item_Type>() : current_capacity(INITIAL_CAPACITY), the_data(new Item_Type[INITIAL_CAPACITY]), num_items(0) { }
Chapter 4: Sequential Containers 15
Implementing vector::push_back (2)
void push_back(const Item_Type& the_value) { // Make sure there is space for the new item. if (num_items == current_capacity) { // Allocate an expanded array reserve(2 * current_capacity); } // Insert the new item. the_data[num_items] = the_value; num_items++;}
Chapter 4: Sequential Containers 16
Implementing vector::reserve
void reserve(size_t new_capacity) { if (new_capacity > current_capacity) { if (new_capacity > 2 * current_capacity) current_capacity = new_capacity; else current_capacity *= 2; // Double the capacity. Item_Type* new_data = new Item_Type[current_capacity]; // Copy the data over. for (size_t i = 0; i < num_items; i++) new_data[i] = the_data[i]; // Free the memory occupied by the old copy. delete[] the_data; // Now point to the new data. the_data = new_data; }}
Chapter 4: Sequential Containers 18
Implementing vector::insert (2)void insert(size_t index, const Item_Type& the_value) { // Validate index. if (index > num_items) { throw std::out_of_range ("index to insert is out of range"); } // Ensure that there is space for the new item. if (num_items == current_capacity) { reserve(2 * current_capacity); // Allocate an expanded array } // Move data from index to num_items - 1 down. for (size_t i = num_items; i > index; i--) { the_data[i] = the_data[i - 1]; } // Insert the new item. the_data[index] = the_value; num_items++;}
Chapter 4: Sequential Containers 20
Implementing vector::erase (2)
void erase(size_t index) { // Validate index. if (index > num_items) { throw std::out_of_range ("index to insert is out of range"); } // Move items below the removed one up. for (size_t i = index + 1; i < num_items; i++) { the_data[i - 1] = the_data[i]; } num_items--;}
Chapter 4: Sequential Containers 21
Implementing vector::operator[]
Item_Type& operator[](size_t index) { // Verify that the index is legal. if (index < 0 || index >= num_items) { throw std::out_of_range ("index to operator[] is out of range"); } return the_data[index];}
Chapter 4: Sequential Containers 22
The copy constructor
• The compiler defines a special constructor, known as the copy constructor, that constructs a copy of a given object.
• This constructor is automatically invoked when objects are passed by value to a function and when values are returned.
• It can also be explicitly invoked. E.G.
vector<Item_Type> v2(v1);will construct the vector v2 to be a copy of v1.
Chapter 4: Sequential Containers 23
Shallow Copy versus Deep Copy
• The copy constructor that is automatically created by the compiler (known as the default copy constructor) makes a copy of each data member.
• Note that the data member the_data in both v1 and v2 point to the same array.
Chapter 4: Sequential Containers 26
Copy Constructor That Makes Deep Copy
vector<Item_Type>(const vector<Item_Type>& other) : current_capacity(other.capacity),
num_items(other.num_items), the_data(new Item_Type[other.current_capacity]) { for (size_t i = 0; i < num_items; i++) the_data[i] = other.the_data[i];}
Chapter 4: Sequential Containers 27
Destructor
• When a vector is constructed, space for the data array is allocated via the new operator.
• All space that is allocated with the new operator should be returned to the free storage pool via the delete operator when it is no longer needed.
• Failure to return allocated memory results in a memory leak and can lead to your program crashing because memory is not available.
• When an object is no longer needed, its destructor is called.
• It is the destructor’s responsibility to free any allocated memory.
Chapter 4: Sequential Containers 28
The vector’s destructor
virtual ~vector<Item_Type>() { delete[] the_data; }
Chapter 4: Sequential Containers 29
The swap function
• The swap function swaps the data fields of two vectors.void swap(vector<Item_Type>& other) { std::swap(num_items, other.num_items); std::swap(current_capacity, other.current_capacity); std::swap(the_data, other.the_data);}
Chapter 4: Sequential Containers 30
The assignment operator
• The statement:v2 = v1;
invokes the assignment operator.• Like the copy constructor, it should make a deep copy.• We can use the copy constructor and swap function to implement the
assignment operator.vector<Item_Type>& operator=(const vector<Item_Type>& other) { // Make a copy of the other vector. vector<Item_Type> the_copy(other); // Swap contents of self with the copy. swap(the_copy); // Return -- upon return the old value will be destroyed. return *this;}
Chapter 4: Sequential Containers 31
The Rule of Three
• A class that dynamically allocates memory (or other resources) should define:• A copy constructor• A destructor• An assignment operator
• Corollary: If a class defines one of:• Copy constructor• Destructor• Assignment operator
then it should define all three.
Chapter 4: Sequential Containers 32
Performance of the vector Class
• Index operator executes in constant time: O(1)• Inserting or removing general elements is linear time: O(n)• Adding at end is (usually) constant time: O(1)
• With our reallocation policy the average is O(1)• The worst case is O(n) because of reallocation
Chapter 4: Sequential Containers 33
Singly-Linked Lists and Doubly-Linked Lists
• The vector insert and erase methods are O(n)• Because they need to shift the underlying array
• Linked list overcomes this:• Add/remove items anywhere in constant time: O(1)
• Each element (node) in a linked list stores:• The element information, of type Item_Type• A link to the next node• A link to the previous node (optional)
Chapter 4: Sequential Containers 34
A List Node
• A node contains:• A data item• One or more links
• A link is a pointer to a list node• The node class is usually defined inside another class:
• It is a hidden inner class• The details of a node should be kept private
Chapter 4: Sequential Containers 36
List Nodes for Singly-Linked Lists#ifndef NODE_H_#define NODE_H_
/** A Node is the building block for a single-linked list. */struct Node { // Data Fields /** The data */ Item_Type data; /** The pointer to the next node. */ Node* next;
// Constructor /** Creates a new Node that points to another Node. @param data_item The data stored @param next_ptr pointer to the Node that is pointed to by the new Node */ Node(const Item_Type& data_item, Node* next_ptr = NULL) : data(data_item), next(next_ptr) {}};#endif
Chapter 4: Sequential Containers 37
struct versus class
• A struct is the same as a class.• Except that the default visibility for a struct is public.
• Generally structs are used to define classes that only contain public data fields.• Constructors may be provided.• Other member functions (operators) are usually not
defined for structs.
Chapter 4: Sequential Containers 38
Inserting a Node into a Single Linked List
Node* bob = new Node("Bob");bob->next = harry->next; // step 1harry->next = bob; // step 2
Chapter 4: Sequential Containers 39
Removing a node from a single-linked list
Node* ptr = tom->next;tom->next = tom->next->next;delete ptr;
Chapter 4: Sequential Containers 40
Doubly-Linked Lists
• Limitations of a singly-linked list include:• Can insert only after a referenced node• Removing node requires pointer to previous node• Can traverse list only in the forward direction
• We can remove these limitations:• Add a pointer in each node to the previous node:
doubly-linked list
Chapter 4: Sequential Containers 42
Inserting into a Double-Linked List
DNode* sharon = new DNode("Sharon");// Link new DNode to its neighborssharon->next = sam; // Step 1sharon->prev = sam->prev; // Step 2
Chapter 4: Sequential Containers 43
Inserting into a Double-Linked List (2)
// Link old predicessor of sam to new predicessor.sam->prev->next = sharon; // Step 3// Link to new predicessor.sam->prev = sharon; // Step 4
Chapter 4: Sequential Containers 44
Removal from a Double-Linked List
harry->prev->next = harry->next; // Step 1harry->next->prev = harry->prev; // Step 2delete harry;
Chapter 4: Sequential Containers 45
Circular Lists
• Circular doubly-linked list:• Link last node to the first node, and• Link first node to the last
• Advantages:• Can easily keep going “past” ends• Can visit all elements from any starting point• Can never fall off the end of a list
• Disadvantage: code must avoid infinite loop!• Can also build singly-linked circular lists:
• Traverse in forward direction only
Chapter 4: Sequential Containers 47
The iterator
• Suppose we want to access each element of a list in a loop:
for (Item_Type index = 0; index < a_list.size(); index++) {
// Do something with next_element, the element // at position index Item_Type next_element = a_list[index]; // not valid}
• The subscripting operator (operator[]) is not defined for the list class.
• Instead an iterator is used to access elements in a list.
Chapter 4: Sequential Containers 49
Using an Iterator
• We use an iterator like a pointer.// Access each list element and process itfor (list<Item_Type>::iterator iter = a_list.begin(); iter != a_list.end(); ++iter) { // Do something with next element (*iter) Item_Type next_element = *iter; . . .}
Chapter 4: Sequential Containers 50
Notes on using iterators in loops
• We test for the end of the loop with the expression:iter != a_list.end()
The order of individual iterator values is not meaningful.Thus iter < a_list.end() is not a meaningful test.
• We increment the iterator using the expression++iter;
The postfix increment operator saves the previous value and then performs the increment. Since this previous value is not used, it is more efficient to use the prefix increment operator.
Chapter 4: Sequential Containers 51
iterator versus const_iterator
• Each standard library container (e.g. the list) provides both an
iterator
• and aconst_iterator
• The operations on them are the same, except• When a const_iterator is dereferenced
(operator*), the value of the item referenced cannot be changed.
Chapter 4: Sequential Containers 53
Iterator Functions
Function Behavior Required for Iterator Type const Item_Type& operator* Returns a reference to the object
referenced by the current iterator position that can be used on the right-hand side of an assignment.
All except output_iterator
Item_Type& operator* Returns a reference to the object referenced by the current iterator position that can be used on the left-hand side of an assignment.
All except input_iterator.
iterator& operator++() Prefix increment operator. All iterators. iterator operator++(int) Postfix increment operator All iterators iterator& operator--() Prefix decrement operator bidirectional_iterator and
random_access_iterator iterator operator--(int) Postfix decrement operator bidirectional_iterator and
random_access_iterator iterator& operator+=(int) Addition-assignment operator random_access_iterator iterator operator+(int) Addition operator random_access_iterator iterator& operator-=(int) Subtraction-assignment operator random_access_iterator iterator operator-(int) Subtraction operator random_access_iterator int operator-(random_access_iterator, random_access_iterator)
Subtraction of two iterators random_access_iterator
Chapter 4: Sequential Containers 54
The list class
• Part of the C++ Standard LibraryFunction Behavior iterator insert(iterator pos, const Item_Type& item)
Inserts a copy of item into the list at position pos. Returns an iterator that references the newly inserted item.
iterator erase(iterator pos) Removes the item from the list at position pos. Returns an iterator that references the item following the one erased.
void remove(const Item_Type& item) Removes all occurrences of item from the list. void push_front(const Item_Type& item) Inserts a copy of item as the first element of the list. void push_back(const Item_Type& item) Adds a copy of item to the end of the list. void pop_front() Removes the first item from the list. void pop_back() Removes the last item from the list. Item_Type& front(); const Item_Type& front() const Gets the first element in the list. Both constant and modifiable
versions are provided. Item_Type& back(); const Item_Type& back() const
Gets the last element in the list. Both constant and modifiable versions are provided.
iterator begin() Returns an iterator that references the first item of the list. const_iterator() begin const Returns a const_iterator that references the first item of
the list. iterator end() Returns an iterator that references the end of the list (one
past the last item). const_iterator end() const Returns a const_iterator that references the end of the list
(one past the last item). void swap(list<Item_Type>& other) Exchanges the contents of this list with the other list. bool empty() const Returns true if the list is empty. size_t size() const Returns the number of items in the list.
Chapter 4: Sequential Containers 55
template<typename Item_Type> class list { private: // Insert definition of nested class DNode here. #include "DNode.h" public: // Insert definition of nested class iterator here. #include "list_iterator.h" // Give iterator access to private members of list. friend class iterator; // Insert definition of nested class const_iterator here. #include "list_const_iterator.h" // Give const_iterator access to private members of list. friend class const_iterator; private: // Data fields /** A reference to the head of the list */ DNode* head; /** A reference to the end of the list */ DNode* tail; /** The size of the list */ size_t num_items;
Implementing KW::list
Chapter 4: Sequential Containers 56
The no-argument constructor
/** Construct an empty list. */list() : head(NULL), tail(NULL), num_items(0) { }
Chapter 4: Sequential Containers 57
The Copy Constructor
/** Construct a copy of a list. */list(const list<Item_Type>& other) list() : head(NULL), tail(NULL), num_items(0) { for (const_iterator itr = other.begin(); itr != other.end(); ++itr) { push_back(*itr); }}
Chapter 4: Sequential Containers 58
The Destructor
/** Destroy a list. */~list() { while (head != NULL) { DNode* current = head; head = head->next; delete current; } tail = NULL; num_items = 0;}
Chapter 4: Sequential Containers 59
The Assignment Operator
/** Assign the contents of one list to another. */list<Item_Type>& operator=(const list<Item_Type>& other) { // Make a copy of the other list. list<Item_Type> temp_copy(other); // Swap contents of self with the copy. swap(temp_copy); // Return -- upon return the old value will be destroyed. return *this;}
Chapter 4: Sequential Containers 60
The push_front function
void push_front(const Item_Type& item) { head = new DNode(item, NULL, head); // Step 1 if (head->next != NULL) head->next->prev = head; // Step 2 if (tail == NULL) // List was empty. tail = head; num_items++;}
Chapter 4: Sequential Containers 62
The push_back function
void push_back(const Item_Type& item) { if (tail != NULL) { tail->next = new DNode(item, tail, NULL); // Step 1 tail = tail->next; // Step 2 num_items++; } else { // List was empty. push_front(item); }}
Chapter 4: Sequential Containers 64
The insert function
iterator insert(iterator pos, const Item_Type& item) { // Check for special cases if (pos.current == head) { push_front(item); return begin(); } else if (pos.current == NULL) { // Past the last node. push_back(item); return iterator(this, tail); } // Create a new node linked before node referenced by pos. DNode* new_node = new DNode(item, pos.current->prev, pos.current); // Step 1 // Update links pos.current->prev->next = new_node; // Step 2 pos.current->prev = new_node; // Step 3 num_items++; return iterator(this, new_node);}
Chapter 4: Sequential Containers 66
The pop_front Function
void pop_front() { if (head == NULL) throw std::invalid_argument ("Attempt to call pop_front() on an empty list"); DNode* removed_node = head; head = head->next; delete removed_node; if (head != NULL) head->prev = NULL; else tail = NULL; num_items--;}
Chapter 4: Sequential Containers 67
The pop_back Function
void pop_back() { if (tail == NULL) throw std::invalid_argument ("Attempt to call pop_back() on an empty list"); DNode* removed_node = tail; tail = tail->prev; delete removed_node; if (tail != NULL) tail->next = NULL; else head = NULL; num_items--;}
Chapter 4: Sequential Containers 68
The erase Functioniterator erase(iterator pos) { if (empty()) throw std::invalid_argument ("Attempt to call erase on an empty list"); if (pos == end()) throw std::invalid_argument ("Attempt to call erase of end()"); // Create an iterator that references the position following pos. iterator return_value = pos; ++return_value; // Check for special cases. if (pos.current == head) { pop_front(); return return_value; } else if (pos.current == tail) { pop_back(); return return_value; } else { // Remove a node in the Item_Typeerior of the list. // Unlink current node. DNode* removed_node = pos.current; removed_node->prev->next = removed_node->next; removed_node->next->prev = removed_node->prev; delete removed_node; return return_value; }}
Chapter 4: Sequential Containers 69
The kw::list<Item_Type>::iteratorData Field Attribute DNode* current A pointer to the current item.
list<Item_Type>* parent A pointer to the list of which this iterator is a member.
Chapter 4: Sequential Containers 70
Data Fields and The Constructorclass iterator { // Give the parent class access to this class. friend class list<Item_Type>; private: // Data fields /** A reference to the parent list */ list<Item_Type>* parent; /** A pointer to the current DNode */ typename list<Item_Type>::DNode* current; // Member functions /** Constructs an iterator that references a specific DNode. Note: this constructor is private. Only the list class can create one from scratch. @param my_parent A reference to the list @param position A pointer to the current DNode */ iterator(list<Item_Type>* my_parent, DNode* position) : parent(my_parent), current(position) {}
Chapter 4: Sequential Containers 71
Dereferencing Operators
/** Returns a reference to the currently referenced item. @return A reference to the currently referenced item @throws std::invalid_argument if this iterator is at end */ Item_Type& operator*() const { if (current == NULL) throw std::invalid_argument("Attempt to dereference end()"); return current->data; }
/** Returns a pointer to the currently referenced item. Item_Type must be a class or struct. This restriction is enforced by the compiler. @return A pointer to the currently referenced item @throws std::invalid_argument If this iterator is at end */ Item_Type* operator->() const { if (current == NULL) throw std::invalid_argument("Attempt to dereference end()"); return &(current->data); }
Chapter 4: Sequential Containers 72
Prefix increment and decrement
iterator& operator++() { /*<snippet id="5" omit="false">*/ if (current == NULL) throw std::invalid_argument("Attempt to advance past end()"); current = current->next; return *this; }
iterator& operator--() { if (current == parent->head) throw std::invalid_argument("Attempt to move before begin()"); if (current == NULL) // Past last element. current = parent->tail; else current = current->prev; return *this; }
Chapter 4: Sequential Containers 73
Postfix increment and decrement
iterator operator++(Item_Type) { // Make a copy of the current value. iterator return_value = *this; // Advance self forward. ++(*this); // Return old value. return return_value; // Return the value prior to increment }
iterator operator--(Item_Type) { // Make a copy of the current value. iterator return_value = *this; // Move self backward. --(*this); // Return old value. return return_value; // Return the value prior to decrement }
Chapter 4: Sequential Containers 74
The const_iterator• Identical to the iterator except that the dereferencing operators
are defined as follows:const Item_Type& operator*() const { if (current == NULL) throw std::invalid_argument ("Attempt to dereference end()"); return current->data;}
const Item_Type* operator->() const { if (current == NULL) throw std::invalid_argument ("Attempt to dereference end()"); return &(current->data);}
Chapter 4: Sequential Containers 75
An Application: Ordered Lists
• Want to maItem_Typeain a list of names• Want them in alphabetical order at all times• Approach: Develop an Ordered_List class
• For reuse, good if can work with other types:
Chapter 4: Sequential Containers 76
Design of the Ordered_List class
Data Field Attribute std::list<Item_Type> a_list A linked list to contain the data Function Behavior
void insert(const Item_Type& item) Inserts item into the list preserving the list’s order
const_iterator begin() Return a const_iterator to the beginning of the list
const_iterator end() Return a const_iterator to the end of the list
size_t size() const Returns the size of the list. public void remove(const Item_Type& item) Removes item from the list.
Chapter 4: Sequential Containers 77
Algorithm for Insertion
1. Find the first item in the list that is greater than or equal to the item to be inserted.
2. Insert the new item before this one.
Chapter 4: Sequential Containers 78
Refinement of algorithm
1.1 Create an iterator that starts at the beginning of the list
1.2 while the iterator is not at the end, and the item at the iterator position is less than the item to be inserted
1.3 Advance the iterator
2. Insert the new item before the current iterator position
Chapter 4: Sequential Containers 80
Ordered_List::insert
void insert(const Item_Type& an_item) { typename std::list<Item_Type>::iterator itr = a_list.begin(); while (itr != a_list.end() && *itr < an_item) ++itr; // itr points to the first item >= an_item or the end. a_list.insert(itr, an_item);}
Chapter 4: Sequential Containers 82
Common Requirements for Containerstypedefs Meaning value_type The type of the object stored in the container. iterator An iterator appropriate for the container. const_iterator A const_iterator appropriate for the container Constructors Purpose X() Default constructor where X is the container type.
X(a) Copy constructor where X is the container type and a is an object of this type.
Functions Behavior ~X() Destructor X& operator=(const X& a) Assignment operator bool operator==(const X& a) const Equality operator bool operator!=(const X& a) const Negation of equals bool operator<(const X& a) const bool operator>(const X& a) const bool operator<=(const X& a) const bool operator>=(const X& a) const
Comparison operators. Compares each item in the two containers. Objects must implement < operator.
void swap(X& a) Swap the contents of this container with the other. iterator begin() const_iterator() begin() const Return an iterator to the beginning of the container iterator end() const_iterator end() const Return an iterator to one past the end of the container size_t size() const Return the number of objects in the container bool empty() const Returns true if the container is empty.
Chapter 4: Sequential Containers 83
Common Requirements for Sequences
Constructors Purpose
X(size_t num, const Item_Type& initial) Constructs the container of type X containing num copies of initial.
X(const_iterator start, const_iterator stop) Constructs the container of type X containing a copy of the items starting with the object referenced by start and continuing up to but not including stop.
Functions Behavior iterator insert(iterator pos, const Item_Type& item)
Insert a copy of item into the container before position pos. Return an iterator referencing the inserted object.
iterator erase(iterator pos) Remove the item referenced by the iterator at pos. Returns an iterator to the item following pos.
Item_Type& front() const Item_Type& front() const Returns a reference to the first item in the container. Item_Type& back() const Item_Type back() const Returns a reference to the last item in the container void push_back(const Item_Type& item) Insert a new item as the last item in the container. void pop_back() Remove the last item from the container
Chapter 4: Sequential Containers 84
Requirements Applicable to Some Sequences
Functions Behavior Required by void push_front(const Item_Type& item) Insert a new item as the first item in the container list, deque
void pop_front() Remove the first item from the container list, deque Item_Type& operator[](size_t index) const Item_Type& operator[](size_t index) const
Randomly access an item in the container via an index.
vector, deque
Item_Type& at(size_t index) const Item_Type& at(size_t index) const
Randomly access an item in the container via an index. If the index is out of bounds (>= size()) throw invalid argument exception.
vector, deque
Chapter 4: Sequential Containers 85
The Standard Library Algorithms
• Function to find an Item_Type in a list<Item_Type>list<Item_Type>::iterator find(list<Item_Type>& a_list,
Item_Type target) { for (list<Item_Type>::iterator itr = a_list.begin(); itr != a_list.end(); ++itr) { if (*itr == target) return itr; } return a_list.end();}
• Observe that this function only works for Item_Type.
Chapter 4: Sequential Containers 86
Template version of find
template <typename Item_Type>list<Item_Type>::iterator find(list<Item_Type>& a_list, const Item_Type& target) { typedef typename list<Item_Type>::iterator iterator; for (iterator itr = a_list.begin(); itr != a_list.end();
++itr) { if (*itr == target) return itr; } return a_list.end();}
• This only works for a vector.• What if we want to search a list?
Chapter 4: Sequential Containers 87
General Version of find
template <typename Iterator, typename Item_Type> Iterator find(Iterator first, Iterator last, const Item_Type& target) { while (first != last) { if (*first == target) return first; } return first;}
• first references the first item in the sequence• last references one past the last item in the sequence
Chapter 4: Sequential Containers 88
The algorithm library
• The standard library header <algorithm> defines several template functions.
• These perform fairly standard operation on sequences within containers• find an item (find)• apply a function to each item (for_each)• copy values from one container to another (copy)• sort the contents of a container (sort)
Chapter 4: Sequential Containers 89
Why use algorithm library
• Most of the algorithms in <algorithm> are fairly simple.• Why not code them in-line in your program yourself?
• No need to “reinvent the wheel”• While they are fairly simple, they have all been
thoroughly tested.• Compiler can generate more optimal code from
standard library
Chapter 4: Sequential Containers 90
Selected Members of <algorithm>
Function Behavior template<typename II, typename F> F for_each(II first, II last, F fun)
Applies the function fun to each object in the sequence. The function fun is not supposed to modify its argument. The iterator argument II is required to be an input iterator. This means that the sequence is traversed only once.
template<typename II, typename T> II find(II first, II last, T target)
Finds the first occurrence of target in the sequence. If not found, last is returned.
template<typename II, typename P> II find_if(II first, II last, P pred)
Finds the first occurrence of an item in the sequence for which function pred returns true. If not found, last is returned.
template<typename FI> FI min_element(FI first, FI last); template<typename FI> FI max_element(FI first, FI last)
Finds the min/ max element in the sequence first..last. FI is a forward iterator.
Chapter 4: Sequential Containers 91
Function Behavior template<typename II, typename OI> OI copy(II first, II last, OI result)
Copies the sequence first..last into result..(result + (last - first)). II is an input iterator, and OI is an output iterator.
template<typename II, typename OI, typename OP> OI transform(II first, II last, OI result, OP op)
Applies op to each element of the sequence first..last and places the result in result..(result + (last - first)).
template<typename II1, typename II2,typename OI, typename BOP> OI transform(II1 first1, II1 last1, II2 first2, OI result, BOP bin_op)
Applies bin_op to each pair of elements of the sequences first1..last1 and first2..(first2 + (last1 - first1)) and places the result in result..(result + (last1 - first1))
template<typename T> void swap(T& a, T& b)
Exchanges the contents of a and b.
template<typename FI1, typename FI2> void iter_swap(FI1 a, FI1 b)
Exchanges the values referenced by iterators a and b.
template<typename BI> void reverse(BI first, BI last)
Reverses the sequence first..last. BI is a bidirectional iterator.
Chapter 4: Sequential Containers 92
Function Behavior template<typename RI> void random_shuffle(RI first, RI last)
Randomly rearranges the contents of first..last. RI is a random-access iterator.
template<typename RI> void sort(RI first, RI last)
Sorts the contents of first..last based on the less-than operator applied to pairs of elements.
template<typename RI, typename COMP> void sort(RI first, RI last, COMP comp)
Sorts the contents of first..last based on the binary operator COMP (a function operator). COMP is a function class that takes two arguments and returns a bool.
template<typename II, typename T> T accumulate(II first, II last, T init)
Computes init plus the sum of the elements in first..last. Note that this function is defined in the header <numeric>.
template<typename II1, typename II2> bool equal(II1 first1, II1 last1, II2 first2); template<typename II1, typename II2, typename BP) bool equal(II1 first1, II1 last2, II2 first2, BP pred)
Compares each element in the sequence first1..last1 to the corresponding elements in the sequence first2..first2 + (last1 - first1). If they are all equal, then this returns true. The second form uses the function object pred to perform the comparison.
Chapter 4: Sequential Containers 93
Copy one a list to a vector
a_vector.resize(a_list.size());copy(a_list.begin(), a_list.end(), a_vector.begin());
Chapter 4: Sequential Containers 94
A more complicated example
// Sort a vectorsort(a_vector.begin(), a_vector.end());// Accumulate the sumint sum = accumulate(a_vector.begin(), a_vector.end(), 0);cout << "Sum is " << sum << endl;// Check first element to see if it is the smallestif (*a_vector.begin() != *min_element(a_vector.begin(), a_vector.end()) cerr << "Error in sort\n";// Check last element to see if it is the largestif (*--a_vector.end() != *max_element(a_vector.begin(), a_vector.end()) cerr << "Error in sort\n";
Chapter 4: Sequential Containers 95
Specializing the swap function
• Swap is defined as:template <typename T>void swap(T& x, T& y) { T temp(x); // Make copy of x x = y; y = temp;}
• What if we call this withswap(vector1, vector2);
• This will expand to:vector<int> temp(vector1); //Make a copy of vector 1vector1 = vector2; //Make a copy of vector 2vector2 = temp; //Make a copy of temp
Chapter 4: Sequential Containers 96
Specializing swap (2)
• The vector (and all other containers) define a member function swap that swaps the contents without copying.
• Therefore, we need to define a swap function for vector (and the other containers) that will call this member function:
template<typename Item_Type>inline void swap(vector<Item_Type>& x, vector<Item_Type& y) { x.swap(y);}
• Then the callswap(vector1, vector2)
will expand tovector1.swap(vector2);
• This is called a template specialization• The definition is placed in the <vector> header.• A specialization of swap is defined for each container.
Chapter 4: Sequential Containers 97
Function Objects
• If f is a function, then the expressionf(x)
• will call this function with the parameter x.• A class may overload the function call operator (operator()).
class Divisible_By { private: int divisor; public: Divisible_By(int x) : divisor(x) {} bool operator()(int x) { return x % divisor == 0; }};
• A class that defines operator() is known as a function class.• An object of a function class is known as a function object.• A function object can be used like a function.
Chapter 4: Sequential Containers 98
The find_if function
• Recall the find function searched a container for a target value.
• The find_if funciton searches a container for a value that satisfies a given condition (predicate)
template<typename Iterator, typename P>Iterator find_if(Iterator first, Iterator last, P pred) { while (first != last) { if (pred(*first)) return first; ++first; } return first;}
Chapter 4: Sequential Containers 99
Using find_if and Divisible_By
// Find the first number divisible by 3list<int>::iterator iter;iter = find_if(list_1.begin(), list_1.end(), Divisible_By(3));if (iter != list_1.end()) cout << "First number divisible by 3 is " << *iter << endl;else cout << "No number is divisible by 3\n";// Find the first number divisible by 5iter = find_if(list_1.begin(), list_1.end(), Divisible_By(5));if (iter != list_1.end()) cout << "First number divisible by 5 is " << *iter << endl;else cout << "No number is divisible by 5\n";// Find the first number divisible by 3