Sequential Containers Based on Koffmann and Wolfgang Chapter
Sequential Containers Based on Koffmann and Wolfgang Chapter 4: Sequential Containers
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 2
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 3
The Vector • An enhancement of the array. • Can be used like an array: • Access a value via an index x = 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 4
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 5
Vector Example (2) Had to slide [2], [3] to [3], [4] my_vector. insert(2, “Doc”); Chapter 4: Sequential Containers 6
Vector Example (3) my_vector. push_back("Dopey"); // add at end Cheap to add at end Chapter 4: Sequential Containers 7
Vector Example (4) my_vector. erase(1); Had to slide [2. . 5] to [1. . 4] lst. set(1, “Sneezy”); Replacement is cheap Chapter 4: Sequential Containers 8
Example Applications of 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 9
Using vector in Phone. Directory 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 10
Using vector in Phone. Directory (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 11
Implementing a vector Class • KW: : vector: simplementation 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 12
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 13
Implementing vector: : push_back Chapter 4: Sequential Containers 14
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 15
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 16
Implementing vector: : insert Chapter 4: Sequential Containers 17
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 18
Implementing vector: : erase Chapter 4: Sequential Containers 19
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 20
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 21
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> v 2(v 1); will construct the vector v 2 to be a copy of v 1. Chapter 4: Sequential Containers 22
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 v 1 and v 2 point to the same array. Chapter 4: Sequential Containers 23
Shallow Copy after v 1. push_back(20) Chapter 4: Sequential Containers 24
Deep Copy of a Vector Chapter 4: Sequential Containers 25
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 26
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 27
The vector’s destructor virtual ~vector<Item_Type>() { delete[] the_data; } Chapter 4: Sequential Containers 28
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 29
The assignment operator • The statement: v 2 = v 1; 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<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 30
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 31
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 32
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 33
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 34
List Nodes for Singly-Linked Lists Chapter 4: Sequential Containers 35
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 36
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 37
Inserting a Node into a Single Linked List Node* bob = new Node("Bob"); bob->next = harry->next; // step 1 harry->next = bob; // step 2 Chapter 4: Sequential Containers 38
Removing a node from a single-linked list Node* ptr = tom->next; tom->next = tom->next; delete ptr; Chapter 4: Sequential Containers 39
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 40
Doubly-Linked Lists, The Diagrams Chapter 4: Sequential Containers 41
Inserting into a Double-Linked List DNode* sharon = new DNode("Sharon"); // Link new DNode to its neighbors sharon->next = sam; // Step 1 sharon->prev = sam->prev; // Step 2 Chapter 4: Sequential Containers 42
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 43
Removal from a Double-Linked List harry->prev->next = harry->next; // Step 1 harry->next->prev = harry->prev; // Step 2 delete harry; Chapter 4: Sequential Containers 44
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 45
Implementing a Circular List Chapter 4: Sequential Containers 46
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 47
The iterator (2) Chapter 4: Sequential Containers 48
Using an Iterator • We use an iterator like a pointer. // Access each list element and process it for (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 49
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 50
iterator versus const_iterator • Each standard library container (e. g. the list) provides both an iterator • and a const_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 51
Iterator Hierarchy Chapter 4: Sequential Containers 52
Iterator Functions Chapter 4: Sequential Containers 53
The list class • Part of the C++ Standard Library Chapter 4: Sequential Containers 54
Implementing KW: : list 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; Chapter 4: Sequential Containers 55
The no-argument constructor /** Construct an empty list. */ list() : head(NULL), tail(NULL), num_items(0) { } Chapter 4: Sequential Containers 56
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 57
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 58
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<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 59
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 60
Adding to the Head of the list Chapter 4: Sequential Containers 61
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 62
Adding to the Tail of the list Chapter 4: Sequential Containers 63
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 64
Adding to the Middle of the list Chapter 4: Sequential Containers 65
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 66
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 67
The erase Function iterator 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_value; } else if (pos. current == tail) { pop_back(); 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_value; } } Chapter 4: Sequential Containers 68
The kw: : list<Item_Type>: : iterator Chapter 4: Sequential Containers 69
Data Fields and The Constructor class 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 70
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 71
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 72
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_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_value; // Return the value prior to decrement } Chapter 4: Sequential Containers 73
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 74
An Application: Ordered Lists • Want to ma. Item_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 75
Design of the Ordered_List class Chapter 4: Sequential Containers 76
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 77
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 78
Inserting "Bill" before "Caryn" Chapter 4: Sequential Containers 79
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 80
Standard Library Containers Chapter 4: Sequential Containers 81
Common Requirements for Containers Chapter 4: Sequential Containers 82
Common Requirements for Sequences Chapter 4: Sequential Containers 83
Requirements Applicable to Some Sequences Chapter 4: Sequential Containers 84
The Standard Library Algorithms • Function to find an Item_Type in a 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 85
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; 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 86
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; } • first references the first item in the sequence • last references one past the last item in the sequence Chapter 4: Sequential Containers 87
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 88
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 89
Selected Members of <algorithm> Chapter 4: Sequential Containers 90
Chapter 4: Sequential Containers 91
Chapter 4: Sequential Containers 92
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 93
A more complicated example // Sort a vector sort(a_vector. begin(), a_vector. end()); // Accumulate the sum int sum = accumulate(a_vector. begin(), a_vector. end(), 0); cout << "Sum is " << sum << endl; // Check first element to see if it is the smallest if (*a_vector. begin() != *min_element(a_vector. begin(), a_vector. end()) cerr << "Error in sortn"; // Check last element to see if it is the largest if (*--a_vector. end() != *max_element(a_vector. begin(), a_vector. end()) cerr << "Error in sortn"; Chapter 4: Sequential Containers 94
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 with swap(vector 1, vector 2); • This will expand to: vector<int> temp(vector 1); //Make a copy of vector 1 = vector 2; //Make a copy of vector 2 = temp; //Make a copy of temp Chapter 4: Sequential Containers 95
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 call swap(vector 1, vector 2) will expand to vector 1. swap(vector 2); • 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 96
Function Objects • If f is a function, then the expression f(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 97
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 98
Using find_if and Divisible_By // Find the first number divisible by 3 list<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 3n"; // Find the first number divisible by 5 iter = 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 5n"; // Find the first number divisible by 3 Chapter 4: Sequential Containers 99
- Slides: 99