1 / 99

Sequential Containers

Sequential Containers. Based on Koffmann and Wolfgang Chapter 4. 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

Download Presentation

Sequential Containers

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Sequential Containers Based on Koffmann and Wolfgang Chapter 4

  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 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

  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) my_vector.push_back("Dopey"); // add at end Cheap to add at end Chapter 4: Sequential Containers

  8. 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

  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 memoryleak 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 vectorinsert 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 1 harry->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 neighbors sharon->next = sam; // Step 1 sharon->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 1 harry->next->prev = harry->prev; // Step 2 delete 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 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

  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

More Related