Classes Advanced Topics iterators friend classes Tapestry pp

  • Slides: 22
Download presentation
Classes: Advanced Topics iterators & friend classes Tapestry pp. 614 -620

Classes: Advanced Topics iterators & friend classes Tapestry pp. 614 -620

Overview & Purpose When we write a container class, such as Linked. List or

Overview & Purpose When we write a container class, such as Linked. List or linkstrinset classes, we normally add all functionality that we think would be useful by the users of the class – For example, Add. In. Order because people may want to use a linked list as an ordered list – or may be just an Add function, etc. Similarly, traversing over a linked list is also needed. While traversing you perform some task on each node (e. g. print the content) – Note that printing is often added to the class (e. g. Linked. List) because it is a fixed and expected task. – However, someone may need to traverse the list for whatever other reasons they want (traverse and print twice the number in each node; maybe silly, but it's just an example) Although it is a good idea to add common, expected and mostly used tasks (such as printing the list) as a member function to the class, we need more generic member functions to traverse the linked list in the main program – be aware that we cannot access private data members of the linked list class as we do in the implementation of member functions This functionality is typically provided as an iterator class, associated with the corresponding container class. In this slide set, we will see how an iterator class is written, for the Linked. List class. – These slides are also useful in strengthening/understanding of class member variables and reference variables / reference return. 2

Goal Often, we use what is called an iterator class to access a container

Goal Often, we use what is called an iterator class to access a container class (e. g. Linkedlist – which contains the data) e. g. In order to access the link list`s elements one by one from outside the class, what can be done? void Print (const Linked. List & list) {. . . for (. . . . ; . . . . ) cout <<. . cout << "size =" << list. length(); }

Iterators In order to access a linkedlist`s nodes from outside the class, you need

Iterators In order to access a linkedlist`s nodes from outside the class, you need to do one of the following: 1) Provide methods in the class to access individual elements – but still this method may require knowing or using some of class functions/structs… Get. Head(), Get. Next(), Get. Info() etc. 2) Write an associated iterator class: - that can access the private members of the Linked. List class without giving client code the right to access private data - a bit more complex but preferable - defined as Friend class to the main container class (in our example Linked. List class) void Print (const Linked. List & list) { Linked. List. Iterator iter (list); for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current(); cout << "size =" << list. length(); }

Iterators & Friend Classes We will show the iterator class concepts over two example

Iterators & Friend Classes We will show the iterator class concepts over two example cases. First we will extend previously developed Linked. List class with integer container so that it now includes a friend iterator class. Later we will go over the iterator class of Tapestry's linkstringset class. First of all, the container class for which we are writing an iterator class, needs to declare the iterator class as friend. The friend class is not limited to iterators only. Any two class declarations that need to be separate, but tightly coupled can be friends.

Friend Class How to define a friend class (e. g. shape. Friend) to a

Friend Class How to define a friend class (e. g. shape. Friend) to a class (e. g. shape): class shape { public: . . . private: . . . friend class shape. Friend; } – Friend declaration can be done anywhere in the class declaration (before public and private, or under public or under private) – shape class grants friend status to shape. Friend (one way) – shape. Friend can acces private data and helper functions (defined in private) of shape • but not vice versa unless reverse friend declaration is made

Linked List Class Declaration (. h file) //Declaration of struct node comes here class

Linked List Class Declaration (. h file) //Declaration of struct node comes here class Linked. List { friend class Linked. List. Iterator; private: node * head; int size; public: Linked. List (); Linked. List (const Linked. List &); ~Linked. List (); //destructor //copy constructor void print. List() const; void add. To. Beginning(int n); void delete. List (); int length() const; //returns # of elements in the list const Linked. List & Linked. List: : operator = (const Linked. List & rhs); node * create. Clone () const; };

Completing the Iterator Class class Linked. List. Iterator { public: //constructor comes here How

Completing the Iterator Class class Linked. List. Iterator { public: //constructor comes here How should the iterator class be defined, so as to work as follows? void Print(const Linked. List & list) { Linked. List. Iterator iter (list); void Init() {. . . } bool Has. More() const {. . . } for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current(); //note the use of const //. . . } int Current() const {. . . } void Next() {. . . } private: //what do we need to have as data members? }; 8

Completing the Iterator Class class Linked. List. Iterator { public: //constructor comes here How

Completing the Iterator Class class Linked. List. Iterator { public: //constructor comes here How should the iterator class be defined, so as to work as follows? void Print(const Linked. List & list) { Linked. List. Iterator iter (list); void Init() {. . . } bool Has. More() const {. . . } for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current(); //note the use of const //. . . } int Current() const {. . . } void Next() {. . . } private: //what do we need to have as data members? //you need to access the list //you need to have a pointer over the list }; 9

Private Data Members of the Iterator Class class Linked. List. Iterator { public: .

Private Data Members of the Iterator Class class Linked. List. Iterator { public: . . private: const Linked. List & my. List; // to access the linked list //reference variable will refer to the linked list which is defined outside, //as opposed to making a local copy of it. //it is const so that it can match const parameters (see Print function) //and also so that we are sure not to change anything node * my. Current; }; // to access the next node of the linked list

Constructor for the Iterator Class Here the complication is how the iterator should be

Constructor for the Iterator Class Here the complication is how the iterator should be constructed, so that it can access the linkedlist’s private data Constructor and member functions are declared and defined together here – Not a rule though; you could have a separate implementation file class Linked. List. Iterator { public: Linked. List. Iterator (const Linked. List & list) : my. List(list), my. Current(NULL) {}. . . //more functions will come private: const Linked. List & my. List; node * }; my. Current; Reference is assigned to a reference variable. No copy constrcutor is invoked; my. List is another name for the linked list (the parameter list) on which the iterator will operate

Completing the Iterator Class class Linked. List. Iterator { public: Linked. List. Iterator(const Linked.

Completing the Iterator Class class Linked. List. Iterator { public: Linked. List. Iterator(const Linked. List & list) : my. List(list), my. Current(NULL) { } Initialize the iterator so void Init() that my. Current shows {. . . the head of the list int Current() const {. . . } void Next() {. . . } private: … //see previous slides }; void Print(const Linked. List & list) { Linked. List. Iterator iter (list); for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current(); //. . . } bool Has. More() const {. . . } How should the iterator class be defined, so as to work as follows? Are we at the end of the list? The info field of the node that my. Current points Advance to the next node. } -----------------struct node { int info; node *next; . . . }; class Linked. List { friend class Linked. List. Iterator; private: }; public: . . . node * head; int size; 12

Completing the Iterator Class class Linked. List. Iterator { public: Linked. List. Iterator(const Linked.

Completing the Iterator Class class Linked. List. Iterator { public: Linked. List. Iterator(const Linked. List & list) : my. List(list), my. Current(NULL) { } void Init() { my. Current = my. List. head; // mycurrent points to first node } bool Has. More() const { return (my. Current != NULL); } int Current() const { return my. Current->info; } void Next() { my. Current = my. Current->next; } private: … //see previous slides }; How should the iterator class be defined, so as to work as follows? void Print(const Linked. List & list) { Linked. List. Iterator iter (list); for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current(); //. . . } -----------------struct node { int info; node *next; . . . }; class Linked. List { friend class Linked. List. Iterator; private: }; public: . . . node * head; int size; 13

Where to Use the Iterator Class //free function void Print(const Linked. List & list)

Where to Use the Iterator Class //free function void Print(const Linked. List & list) { Linked. List. Iterator iter (list); for (iter. Init(); iter. has. More(); iter. Next()) cout << iter. Current() << endl; cout << "size = " << list. length() << endl; } int main() { Linked. List list 1; for (int k=0; k < 4; k++) { list 1. add. To. Beginning(k+1); } 4 Print(list 1); 3 } 2 1 size = 4 Linked. List. Iterator(const Linked. List& list) : my. List(list), my. Current(NULL) {} Constructor of the iterator class is called. iter. my. List refers to list so that iter object is bound to the linked list object list In general, an iterator object is bound to a particular container (e. g. linked list) when the iterator is constructed! See Linked. List. Iterator. h and. cpp, and Linked. List. Iteratordemo. cpp for details

Some Issues about the Iterator Class Do we have another. h file for the

Some Issues about the Iterator Class Do we have another. h file for the iterator class? – We could have, but preferred to have both Linked. List and its iterator class in the same. h file What happens if you change head node of the list after binding to the iterator (i. e. after creating the iterator object)? – Let us see the answer on the code How can you change the value in a node using iterator? – Let us see the answer on the code What about copy constructor and destructor for the iterator class? – We did not write and that was on purpose We do not want deep copy here; the iterator object must not copy the linked list – It operates on the existing list; thus the reference of the list is used. – Only shallow copy, thus default copy constructor is enough Since we use the existing list in the iterator class, destructing the iterator object is catastrophic. – Let's see the effect of having destructor on the code

Iterator Class for Link. String. Set Linked. String. Set is a Tapestry Class for

Iterator Class for Link. String. Set Linked. String. Set is a Tapestry Class for a linked list with string container. It works in set manner; that is, the insertion function first check if an element exists in the list. If so, it does not add. If not, adds. The implementation uses dummy node. The dummy node is the first node of the list and it does not contain real data. The first real node is the next of dummy node. The use of dummy node makes sure that the list is never empty (head does not point to NULL) list object my. First my. Size dummy a b c NULL 3 The Iterator class of Link. String. Set is more or less similar to the other one that we have seen, but there are some differences due to the design of Link. String. Set. We will see them now.

Link. String. Set. h class Link. String. Set { public: // constructors. . .

Link. String. Set. h class Link. String. Set { public: // constructors. . . // accessors int size() const; . . . // # elements in set // mutators. . . friend class Link. String. Set. Iterator; private: struct Node //node definition is embedded in class { string info; Node * next; . . . }; . . . Node * my. First; //head of the list int my. Size; //number of elements in the list };

Constructor and Private Data Members for the Iterator Class Having the node definition in

Constructor and Private Data Members for the Iterator Class Having the node definition in the Link. String. Set class slightly changes the private data members as compared to the case of other Linked. List class iterator. class Link. String. Set. Iterator { public: Link. String. Set. Iterator (const Link. String. Set & list) : my. List(list), my. Current(NULL) {} private: const Link. String. Set & my. List; Alternative 1 (more usable, if you will frequently refer to Node): typedef Link. String. Set: : Node; Node * my. Current; Alternative 2 (more clear): Link. String. Set: : Node * }; my. Current; //define a new type name // based on an already defined type //pointer to a linklist node //refer to Link. List class’s node type, rather // than defining a new type

Completing the Iterator Class class Link. String. Set. Iterator { public: Link. String. Set.

Completing the Iterator Class class Link. String. Set. Iterator { public: Link. String. Set. Iterator(const Link. String. Set & list) : my. List(list), my. Current(NULL) {} void Init() { my. Current = my. List. my. First->next; // mycurrent points to first real node } bool Has. More() const { return (my. Current != NULL); } string Current() const { return my. Current->info; } class Link. String. Set { private: struct Node { string info; Node * next; } Node * my. First; . . . //points to dummy header public: . . . friend class Link. String. Set. Iterator; }; void Next() { my. Current = my. Current->next; } private: … //see previous slide }; 19

Using the Link. String. Set. Iterator Class Not different than the other Iterator class

Using the Link. String. Set. Iterator Class Not different than the other Iterator class void Print(const Link. String. Set & list) { Link. String. Set. Iterator it (list); for (it. Init(); it. has. More(); it. Next()) cout << it. Current(); cout << ``size =`` << list. Size(); } int main() { Link. String. Set a; a. insert("apple"); a. insert("cherry"); cout << "a : "; Print(a); //cherry apple 2 } See linkedstringset. h and. cpp, and linksetdemo. cpp for details

Using the Link. String. Set. Iterator Class - 2 Updating the values stored in

Using the Link. String. Set. Iterator Class - 2 Updating the values stored in the nodes – e. g. append " seed" the end of the info field of each node in the list Link. String. Set c; c. insert("watermelon"); c. insert("apricot"); Link. String. Set. Iterator itr(c); for (itr. Init(); itr. Has. More(); itr. Next()) { itr. Current(). append(" seed"); } cout << "after updatenc : "; Print(c); after update c : apricot What's output? watermelon ----- size = 2 Why it did not work out? string Current() const { return my. Current->info; } Current returns a copy due to return type string. What's updated is this copy, not the list's element. How can we solve it? See next slide for one solution. See linkedstringset. h and. cpp, and linksetdemo. cpp for details

Using the Link. String. Set. Iterator Class - 3 Solution: Let the Current ()

Using the Link. String. Set. Iterator Class - 3 Solution: Let the Current () function return a reference to the current node's string info string & Current() const { return my. Current->info; } When the return value of Current() is updated the current list element is updated. Link. String. Set c; c. insert("watermellon"); c. insert("apricot"); Link. String. Set. Iterator itr(c); for (itr. Init(); itr. Has. More(); itr. Next()) { itr. Current(). append(" seed"); } cout << "after updatenc : "; Print(c); What's output? See linkedstringset. h and. cpp, and linksetdemo. cpp for details after update c : apricot seed watermelon seed ----- size = 2