sequential containers based on koffmann and wolfgang chapter 4

99
Sequential Containers Based on Koffmann and Wolfgang Chapter 4

Post on 20-Dec-2015

222 views

Category:

Documents


0 download

TRANSCRIPT

Sequential Containers

Based on Koffmann and Wolfgang

Chapter 4

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 14

Implementing vector::push_back

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 17

Implementing vector::insert

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 19

Implementing vector::erase

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 24

Shallow Copy after v1.push_back(20)

Chapter 4: Sequential Containers 25

Deep Copy of a Vector

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 35

List Nodes for Singly-Linked Lists

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 41

Doubly-Linked Lists, The Diagrams

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 46

Implementing a Circular List

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 48

The iterator (2)

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 52

Iterator Hierarchy

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 61

Adding to the Head of the list

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 63

Adding to the Tail of the list

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 65

Adding to the Middle of the list

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 79

Inserting "Bill" before "Caryn"

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 81

Standard Library Containers

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