Lab 04 - Iterator.

Slides:



Advertisements
Similar presentations
Overloading Operators Overloading operators Unary operators Binary operators Member, non-member operators Friend functions and classes Function templates.
Advertisements

Lecture 3 Feb 4 summary of last week’s topics and review questions (handout) Today’s goals: Chapter 1 overview (sections 1.4 to 1.6) c++ classes constructors,
. Templates. Example… A useful routine to have is void Swap( int& a, int &b ) { int tmp = a; a = b; b = tmp; }
Templates and the STL.
 2006 Pearson Education, Inc. All rights reserved Classes: A Deeper Look.
COP3530 Data Structures600 Stack Stack is one the most useful ADTs. Like list, it is a collection of data items. Supports “LIFO” (Last In First Out) discipline.
 2006 Pearson Education, Inc. All rights reserved Classes: A Deeper Look, Part 2.
CS 11 C++ track: lecture 5 Today: Member initialization lists Linked lists friend functions.
©Fraser Hutchinson & Cliff Green C++ Certificate Program C++ Intermediate Operator Overloading.
Iterator for linked-list traversal, Template & STL COMP171 Fall 2005.
A Doubly Linked List prevnextdata There’s the need to access a list in reverse order header dnode.
Operator Overloading. Binary operators Unary operators Conversion Operators –Proxy Classes bitset example Special operators –Indexing –Pre-post increment/decrement.
Operator Overloading Moshe Fresko Bar-Ilan University Object Oriented Programing
 Binary operators  Unary operators  Conversion Operators  Proxy Classes (simulating a reference) ▪ bitset example  Special operators  Indexing 
CSE1002 – Problem Solving with Object Oriented Programming
Operator Overloading CS 3370 – C++ Chapter 14.
Operator Overloading Introduction
Yan Shi CS/SE 2630 Lecture Notes
Motivation for Generic Programming in C++
CS212: Object Oriented Analysis and Design
Mark Redekopp David Kempe
Introduction to Linked Lists
C++ Templates.
Lecture 7-2 : STL Iterators
Motivation and Overview
Chapter 4 Linked Lists.
A Doubly Linked List There’s the need to access a list in reverse order prev next data dnode header 1.
Monday, March 19, 2018 Announcements… For Today… For Next Time…
Tuesday, February 20, 2018 Announcements… For Today… 4+ For Next Time…
Friday, February 16, 2018 Announcements… For Today… 4.5-, pgs. 252
Wednesday, February 14, 2018 Announcements… For Today…
Lab 08 - BST.
Introduction to Linked Lists
(5 - 1) Object-Oriented Programming (OOP) and C++
C++ Templates L03 - Iterator 10 – Iterator.
10 – Iterators C++ Templates 4.6 The Iterator pgs
Chapter 16-2 Linked Structures
Lecture 7-2 : STL Iterators
Linked Lists.
ADT Implementations: Templates and Standard Containers
Lab 04 – Linked List.
Object Oriented Programming COP3330 / CGS5409
Lab 03 - Iterator.
Operator Overloading; String and Array Objects
Chapter 3 Lists, Stacks, and Queues Abstract Data Types, Vectors
priority_queue<T>
[Chapter 4; Chapter 6, pp ] CSC 143 Linked Lists [Chapter 4; Chapter 6, pp ]
Overview of C++ Overloading
Doubly Linked List Implementation
Operator Overloading, Friends, and References
Chapter 16 Linked Structures
(5 - 1) Object-Oriented Programming (OOP) and C++
Standard Template Library (STL)
C++ Templates L03 - Iterator 10 – Iterator.
9-10 Classes: A Deeper Look.
Lecture 8-2 : STL Iterators and Algorithms
C++ Templates L03 - Iterator 10 – Iterator.
COP 3330 Object-oriented Programming in C++
Lists.
Operator Overloading; String and Array Objects
Lab4 problems More about templates Some STL
CS410 – Software Engineering Lecture #5: C++ Basics III
Lesson 25 Miscellaneous Topics
Doubly Linked List Implementation
9-10 Classes: A Deeper Look.
Lab 03 – Linked List.
四時讀書樂 (春) ~ 翁森 山光照檻水繞廊,舞雩歸詠春風香。 好鳥枝頭亦朋友,落花水面皆文章。 蹉跎莫遣韶光老,人生唯有讀書好。
The List Container and Iterators
16 – Sequential Containers
C++ Templates L04 - Iterator 10 – Iterator.
Presentation transcript:

Lab 04 - Iterator

Nested Classes

Nested Class Iterators A nested (inner) class is a class which is declared inside an enclosing (outer) class. While an inner class is a member of the outer class and has the same access rights as any other outer class member, An inner class does not have an implicit reference to an instance of the outer class. If the inner class needs access to outer class members, you must pass an outer class reference (pointer) to the inner class, then the inner class can reference anything in the outer class instance. class Outer { class Inner Outer* ptr; Inner(Outer* p) : ptr(p) {} };

Nested Class Iterators You make the parent-child relationship manually thru a reference/pointer or constructor arguments. class Outer { private: int var; public: Outer() : var(0) {} class Inner Outer* optr; Inner(Outer* op) : optr(op) {} void add(int x) { optr->var += x; } }; Inner newInner() { return Outer::Inner(this); } int getVar() { return var; } int main() { Outer outer; Outer::Inner inner = outer.newInner(); inner.add(20); cout << outer.getVar(); return 0; } 20

Nested Class Nested classes increase encapsulation. Iterators Nested classes increase encapsulation. Nested classes can lead to more readable and maintainable code – useful for developing object models in your component. Iterators are generally implemented as nested classes. class MyClass { class Iterator }; Iterator begin() { return MyClass::Iterator(this, start); } Iterator end() { return MyClass::Iterator(this, 0); }

Iterators

Array Container Access Iterators By Index #include <iostream> using namespace std; int main() { int dog[] = { 1, 2, 3, 4 }; int* dptr = dog; for (size_t i = 0; i < 4; ++i) cout << *dptr++ << endl; } return 0; By Pointer #include <iostream> using namespace std; int main() { int dog[] = { 1, 2, 3, 4 }; for (size_t i = 0; i < 4; ++i) cout << dog[i] << endl; } return 0;

 Other Containers #include <iostream> #include <vector> Iterators #include <iostream> #include <vector> using namespace std; int main() { vector<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); for (size_t i = 0; i < numbers.size(); ++i) cout << numbers[i] << endl; } return 0; #include <iostream> #include <list> using namespace std; int main() { list<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); for (size_t i = 0; i < numbers.size(); ++i) cout << numbers[i] << endl; } return 0; 

Other Containers w/Iterator Iterators #include <iostream> #include <vector> using namespace std; int main() { vector<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); vector<int>::iterator iter = numbers.begin(); while (iter != numbers.end()) cout << *iter++ << endl; } return 0; #include <iostream> #include <list> using namespace std; int main() { list<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); list<int>::iterator iter = numbers.begin(); while (iter != numbers.end()) cout << *iter++ << endl; } return 0;

Why Use an Iterator? Those who avoid using iterators: Assume your container has index ([]), at, and increment (++,--) operators. Assume your container elements can be randomly accessed, are contiguous, and same size (only true for vectors...) Have to write their own versions of common algorithms (ie., sort or reverse) Iterators bring you closer to container independence. You're not making assumptions about random-access ability, storage format, efficiency of operations such as size(), or most algorithms. You only need to know that the container has iterator capabilities. Iterators enhance your code further with standard algorithms. Depending on what it is you're trying to achieve, you may elect to use for_each(), find(), replace(), partition(), search(), transform(), sort(), … By using a standard algorithm rather than an explicit loop you're avoiding re-inventing the wheel. Your code is likely to be more efficient (given the right algorithm is chosen), correct, and reusable.

The STL Iterator Approach Iterators #include <iostream> #include <list> using namespace std; int main() { list<int> myList; myList.push_back(1); myList.push_back(2); myList.push_back(3); myList.push_back(4); list<int>::iterator iter = myList.begin(); while (iter != myList.end()) cout << *iter << " "; ++iter; } return 0; iter "points" to first element in myList. myList.end() "points" to something NOT in myList. Dereference iter to access myList elements.

Prefix / Postfix Unary Operators

Overload Prefix Operator Iterators The prefix and postfix versions of the unary increment and decrement operators can all be overloaded. To allow both unary prefix and postfix increment usage, each overloaded operator function must have a distinct signature (so that the compiler can determine which version of "++" is intended.) When the compiler sees the pre-increment expression on a Data data type: Data myData; ++myData; the compiler generates the member function call: myData.operator++() The prototype for this operator function would be: Data& operator++();

Overload Postfix Operator Iterators Overloading the postfix increment operator presents a challenge because the compiler must be able to distinguish between the signatures of the overloaded prefix and postfix increment operator functions. The convention that has been adopted in C++ is that when the compiler sees the post-incrementing expression object++, it generates the member function call: myData.operator++(0) The prototype for this operator function would be: Data operator++(int); The argument 0 is strictly a "dummy value" that enables the compiler to distinguish between the prefix and postfix increment operator functions.

Overload Postfix Operator Iterators Postfix operators are expressions that yield the original value and then modify the object. Operator overloads are functions and thus all side effects must take place before the function completes. The only way of attaining the required semantics is by copying the initial state of the object before applying the change. The original state must be returned by value. if a reference was returned, the evaluation of the expression would yield the state of the object after the function completes. A typical implementation of postfix involves Creating a temporary object local to the function, Incrementing the originally passed object using the prefix operator, Then return the temporary object by value, not by reference. The local object is guaranteed to be alive only within the scope of the function and any access to it beyond this scope will result in Undefined Behavior.

Prefix and Postfix Operators Iterators // prefix (return by reference) MyClass& operator++() { // implement increment logic, return reference to it. return *this; } // postfix (return by value) MyClass operator++(int) // the dummy int disambiguates { MyClass tmp(*this); operator++(); // prefix-increment this instance return tmp; // return value before increment } **NOTE: The return value of the overloaded postfix operator must be a non-reference, because we can’t return a reference to a local variable that will be destroyed when the function exits. Postfix operators are typically less efficient than the prefix operators because of the added overhead of instantiating a temporary variable and returning by value instead of reference.

Doggy Example… Prefix incrementor Postfix incrementor Output? Iterators #include <iostream> using namespace std; class Dog { private: int value; public: Dog() : value(0) {} Dog& operator++() { ++value; return *this; } Dog operator++(int) { Dog temp(*this); operator++(); return temp; } friend ostream& operator<<(ostream& os, Dog d) { return os << d.value; } }; int main() Dog dog; cout << dog++ << endl; cout << ++dog << endl; return 0; } Prefix incrementor Postfix incrementor Output?

You may freely use the code in any way you deem useful. ****Disclaimer**** The following code examples are flawed and incomplete, but demonstrate how an iterator class might be implemented. You may freely use the code in any way you deem useful. Example Iterator

Step 1 – Verify LinkedList Class Iterators template<typename T> class LinkedList { private: struct Node T data; Node* next; Node(const T& d, Node* n) : data(d), next(n) { } }; Node* head; public: LinkedList() : head(NULL) { } ~LinkedList() { ... } void push_front(const T& value) { head = new Node(value, head); } int main() { LinkedList<int> myList; myList.push_front(4); myList.push_front(3); myList.push_front(2); myList.push_front(1); cout << myList << endl; return 0; } Be sure to use a destructor to free Nodes! string toString(void) const { stringstream out; Node* ptr = head; while (ptr != NULL) out << " " << ptr->data; ptr = ptr->next; } return out.str(); } // end toString() friend std::ostream& operator<< (std::ostream& os, const LinkedList<T>& linkedList) os << linkedList.toString(); return os; } // end operator<< Remember, every class needs a toString and a Friend!

Step 2 – Add a Nested Iterator Iterators template<typename T> class LinkedList { private: struct Node T data; Node* next; Node(const T& d, Node* n) : data(d), next(n) { } }; Node* head; public: LinkedList() : head(NULL) { } ~LinkedList() { ... } void push_front(const T& value) { head = new Node(value, head); } class Iterator Node* node; Iterator(Node* head) : node(head) { } ~Iterator() {} bool operator!=(const Iterator& rhs) const { return node != rhs.node; } Iterator& operator++() { node = node ? node->next : NULL; return *this; } T& operator*() const { return node->data; } Iterator begin(void) { return LinkedList<T>::Iterator(head); } Iterator end(void) { return LinkedList<T>::Iterator(NULL); } int main() { LinkedList<int> myList; myList.push_front(4); myList.push_front(3); myList.push_front(2); myList.push_front(1); cout << myList << endl; LinkedList<int>::Iterator iter = myList.begin(); while (iter != myList.end()) cout << *iter << " "; ++iter; } return 0; MyList Iterator

Step 3 – Implement Functionality Iterators template<typename T> class LinkedList { private: struct Node { ... }; Node* head; public: LinkedList() : head(NULL) { } ~LinkedList() { ... } void push_front(const T& value) { head = new Node(value, head); } class Iterator { ... }; Iterator begin(void) { return LinkedList<T>::Iterator(head); } Iterator end(void) { return LinkedList<T>::Iterator(NULL); } /** Return iterator pointing found value in linked list */ Iterator find(Iterator first, Iterator last, const T& value) { ... } /** Return iterator pointing to inserted value in linked list */ Iterator insert(Iterator position, const T& value) { ... } Iterator insert_after(Iterator position, const T& value) { ... } /** Return iterator pointing to next item after deleted node linked list */ Iterator erase(Iterator position) { ... } /** Replace first found old_value(s) with new_value */ void replace(Iterator first, Iterator last, const T& old_value, const T& new_value) { ... } }; MyList Iterator

Output of Iterator Lab Insert Groot. happy am I Iterate [I] [am] Iterators Insert Groot. happy am I Iterate [I] [am] [happy] [Groot.] PrintList I am happy Groot. InsertAfter happy happy OK PrintList I am happy happy Groot. InsertBefore very happy OK PrintList I am very happy happy Groot. Find happy OK Replace happy sad OK PrintList I am very sad sad Groot. Erase sad OK PrintList I am very sad Groot. [very] [sad]