====== STL Iterators ====== Μία κλάση τύπου //iterator// έχει την δυνατότητα διάτρεξης μιας άλλης κλάσης που αντιπροσωπεύει μία δομή δεδομένων (τη γνωρίσαμε ως //container//). Η κλάση //iterator// γνωρίζει την εσωτερική δομή της κλάσης τύπου //container// και υλοποιεί τον αλγόριθμο για να διαπεράσει το σύνολο των στοιχείων της. Κάθε //iterator// περιέχει κατ'ελάχιστο ένα δείκτη προς το τρέχον στοιχείο προς διάτρεξη του //container// στον οποίο αναφέρεται. Όλοι οι //iterators// ανεξάρτητα από τον τύπο τους υποστηρίζουν τις λειτουργίες: * ''T::iterator it(other_it)'': copy-constructor * ''it = other_it'': copy-assignment * iterator destructor * ''++it'' ή ''it++'': μετακίνηση κατά μία θέση μέσα στον //container// και επιστροφή ενός //iterator// που δείχνει στην τρέχουσα θέση ή στην προηγούμενη θέση του //container// αντιστοίχως. * Υπερφορτώνουν τους τελεστές ''=='' και ''!='' που υλοποιούν τη σύγκριση ως προς την ισότητα/ανισότητα δύο //iterators//. Δύο //iterators// είναι ίσοι εάν δείχνουν στην ίδια θέση του ιδίου //container// Οι κατηγορίες (τύποι)των //iterator// είναι οι παρακάτω. Ένας //iterator// μπορεί να ανήκει σε περισσότερους από ένα τύπους. * **Input Iterator:** Πρόκειται για έναν //iterator// από τον οποίο μπορούμε μόνο να διαβάσουμε το τρέχον στοιχείο της δομής στο οποίο δείχνει. Πρόκειται για έναν //iterator// ανάγνωσης του περιεχόμενου της δομής. Όλοι οι διαθέσιμοι //iterators// είναι //Input Iterators//. Παράδειγμα: #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::const_iterator it=ints.cbegin(); it!=ints.cend(); it++) cout << *it << " "; cout << endl; } * **Output Iterator:** Πρόκειται για έναν //iterator// μέσω του οποίου μπορούμε να διαβάσουμε και να γράψουμε στο τρέχον στοιχείο του //container//. Οι //iterators// που στον τύπο τους περιέχουν το συνθετικό //const_// (const_iterator, const_reverse_iterator) δεν ανήκουν στη συγκεκριμένη κατηγορία. Παραδείγματα: #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::iterator it=ints.begin(); it!=ints.end(); it++) { *it += 10; // we modify container here cout << *it << " "; } cout << endl; for(vector::const_iterator it=ints.cbegin(); it!=ints.cend(); it++) { // *it += 10; // this is not allowed for a const_iterator (only input iterator) cout << *it << " "; } } * **Bidirectional Iterator:** Πρόκειται για iterators που εκτός των πράξεων ''++it'', ''it++'' υποστηρίζουν και τις πράξεις ''%%--%%it'', ''it%%--%%''. //Bidirectional Iterators// είναι όλοι οι //iterators// με εξαίρεση εκείνούς που ανήκουν σε //Unordered Associative Containers// και οι //iterators// του //container forward_list// (απλά συνδεδεμένη λίστα). Παραδείγματα: #include #include #define SIZE 10 using namespace std; int main() { vector ints(SIZE); for(int i=0; i::const_iterator it = ints.cend(); // starting from the end of the container while(true) { --it; // move to the previous element cout << *it << " "; if(it == ints.cbegin()) // stop when we reach the first element break; } cout << endl; } Όταν μία κλάση υποστηρίζει //bidirectional iterators// τότε εκτός από τις μεθόδους //begin//, //end// (και τις συμμετρικές τους για //const iterators// //cbegin//, //cend//), διαθέτει και τις μεθόδους //rbegin//, //rend// (και τις συμμετρικές τους //crbegin//,//crend//). Οι iterators που επιστρέφονται από τις μεθόδους αυτές έχουν την ιδιότητα να διατρέχουν τη δομή του //container// από το τέλος προς την αρχή. Το παρακάτω παράδειγμα είναι αποκαλυπτικό της χρήσης αυτών των //iterator//. Σημειώστε ότι η διάτρεξη της λίστας από το τέλος προς την αρχή γίνεται με τη βοήθεια του τελεστή %% ++ %%, δηλαδή σε κάθε βήμα αυξάνει ο //reverse_iterator// κατά ένα. #include #include #define SIZE 10 int main () { std::vector myvector (SIZE); // SIZE default-constructed ints int i=0; for (std::vector::reverse_iterator rit = myvector.rbegin(); rit!= myvector.rend(); ++rit) *rit = ++i; std::cout << "myvector contains:"; for (std::vector::iterator it = myvector.begin(); it != myvector.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; } * **Random Access Iterators:** Πρόκειται για //iterators// που υποστηρίζουν την πράξη της πρόσθεσης και τις αφαίρεσης μεταξύ του //iterator// και ενός ακεραίου αριθμού (η πράξη είναι ανάλογη της αριθμητικής δεικτών). Οι //iterators// αυτής της κατηγορίας υποστηρίζονται από τους //Sequence Containers// //array//, //vector// και //deque// αλλά όχι από τους //containers list// και //forward_list//. Παράδειγμα: #include #include #define SIZE 10 using namespace std; int main() { vector ints(SIZE); for(int i=0; i::const_iterator it = ints.cend(); // starting past the end of the container while(it > ints.cbegin()) { it -= 2; // move back by 2 elements cout << *it << " "; if(it <= ints.cbegin()) // stop when we reach or we have passed the first element break; } cout << endl; } ===== Διάτρεξη οποιαδήποτε δομής με χρήση iterator ===== Τα παρακάτω παραδείγματα εφαρμόζονται σε //container// τύπου [[http://www.cplusplus.com/reference/vector/vector|vector]], όμως ο κώδικας της διάτρεξης είναι κοινός για όλους τους //containers//. ==== Διαπέραση από την αρχή προς το τέλος χωρίς μεταβολή του container ==== Η διαπέραση από την αρχή προς το τέλος υποστηρίζεται από το σύνολο των //iterators//. #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::const_iterator it=ints.cbegin(); it!=ints.cend(); it++) cout << *it << " "; cout << endl; } **Σημαντική Παρατήρηση:** Η συναρτήσεις [[http://www.cplusplus.com/reference/array/array/cbegin/|cbegin]] και [[http://www.cplusplus.com/reference/array/array/begin/|begin]] επιστρέφουν ένα //iterator// που δείχνει στο πρώτο στοιχείο του //container//. Αντίστοιχα, οι συναρτήσεις [[http://www.cplusplus.com/reference/array/array/cend/|cend]] και [[http://www.cplusplus.com/reference/array/array/end/|end]] επιστρέφουν ένα //iterator// που δείχνει αμέσως μετά το τελευταίο στοιχείο του //container//. O //iterator// διατρέχει τα στοιχεία από την αρχή προς το τέλος του //container//. Η παρακάτω εικόνα είναι ενδεικτική για τις θέσεις του //vector// που επιστρέφουν οι συναρτήσεις //begin()// και //end()//. {{ :cpp:stl:vector02.png?600 |}} Χρησιμοποιούμε τις συναρτήσεις [[http://www.cplusplus.com/reference/vector/vector/cbegin/|cbegin]] και [[http://www.cplusplus.com/reference/vector/vector/cend/|cend]], διότι οι συγκεκριμένες επιστρέφουν αντικείμενο τύπου ''const_iterator'' και όχι ''iterator''. Στην περίπτωση που δεν θέλουμε να μεταβάλλουμε τα περιεχόμενα του υφιστάμενου //container// η χρήση //const_iterator// είναι ασφαλέστερη διότι ο //compiler// θα εμφανίσει λάθος σε οποιαδήποτε προσπάθεια μεταβολής. ==== Διαπέραση από το τέλος προς την αρχή χωρίς μεταβολή του container ==== Η διαπέραση από τον τέλος προς την αρχή υποστηρίζεται μόνο από //bidirectional_iterators//. //Iterators// αυτού του τύπου διαθέτουν μόνον οι κλάσεις //array, list, vector, dequeue, set// και //map//. Δεν τους υποστηρίζουν οι κλάσεις //forward_list//, //unordered_set// και //unordered_map//. #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::const_reverse_iterator it=ints.crbegin(); it!=ints.crend(); it++) cout << *it << " "; cout << endl; } **Παρατήρηση:** Η συναρτήσεις [[http://www.cplusplus.com/reference/array/array/crbegin/|crbegin]] και [[http://www.cplusplus.com/reference/array/array/rbegin/|rbegin]] επιστρέφουν ένα //reverse_iterator// που δείχνει στο τελευταίο στοιχείο του //container//. Αντίστοιχα, οι συναρτήσεις [[http://www.cplusplus.com/reference/array/array/crend/|crend]] και [[http://www.cplusplus.com/reference/array/array/rend/|rend]] επιστρέφουν ένα //reverse_iterator// που δείχνει αμέσως πριν από το πρώτο στοιχείο του //container//. Ο //reverse_iterator// διατρέχει τα στοιχεία από το τέλος προς την αρχή του //container//. Ομοίως, χρησιμοποιούμε τις συναρτήσεις [[http://www.cplusplus.com/reference/vector/vector/crbegin/|crbegin]] και [[http://www.cplusplus.com/reference/vector/vector/crend/|crend]], διότι οι συγκεκριμένες επιστρέφουν ''const_reverse_iterator'' και όχι ''reverse_iterator''. Στην περίπτωση που δεν θέλουμε να μεταβάλλουμε τα περιεχόμενα του υφιστάμενου //container// η χρήση //const_reverse_iterator// είναι ασφαλέστερη για τους λόγους που τεκμηριώθηκαν παραπάνω. ==== Διάτρεξη από την αρχή προς το τέλος και μεταβολή του container ==== #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::iterator it=ints.begin(); it!=ints.end(); it++) { *it += 10; // we modify container here cout << *it << " "; } cout << endl; } ==== Διάτρεξη από το τέλος προς την αρχή και μεταβολή του container ==== #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(vector::reverse_iterator it=ints.rbegin(); it!=ints.rend(); it++) { *it += 10; // we modify container here cout << *it << " "; } cout << endl; } Κατά την δήλωση του //iterator// μπορείτε να χρησιμοποιήσετε τον πριοσδιοριστή ''auto'' αντί για τον πλήρη τύπο του //iterator//. O //compiler// έχει την "εξυπνάδα" να εξάγει τον τύπο δεδομένων της μεταβλητής που δηλώνεται μέσω του //keyward// ''auto'' από τον τύπο δεδομένων που βρίσκεται στα δεξιά του τελεστή %%=%%. Στα παραπάνω παραδείγματα, ο τύπος στα δεξιά του τελεστή %%=%% προσδιορίζεται από τον επιστρεφόμενο τύπο των συναρτήσεων //begin//, //rbegin// κλπ. Στο προηγούμενο παράδειγμα, η διάτρεξη από το τέλος προς την αρχή με μεταβολή του //container// θα μπορούσε να γραφεί εναλλακτικά (και συντομότερα) ως εξής: #include #include #define SIZE 10 using namespace std; int main() { vector ints; for(int i=0; i<10; i++) ints.push_back(SIZE-i); for(auto it=ints.rbegin(); it!=ints.rend(); it++) { *it += 10; // we modify container here cout << *it << " "; } cout << endl; } ===== Κοινές συναρτήσεις για όλους τους iterators ===== Για όλους τους //iterators// είναι διαθέσιμη η συνάρτηση [[http://www.cplusplus.com/reference/iterator/advance/|advance]] η οποία μετακινεί τον //iterator// κατά όσες θέσεις προσδιορίζει το δεύτερο όρισμα (μπορεί να πάρει και αρνητικές τιμές για //Bidirectional// και //Random Access// //iterators//). Η συνάρτηση λειτουργεί με χρήση του τελεστή ''++'', μετακινώντας τον //iterator// κατά μία θέση κάθε φορά, όσες φορές απαιτείται. Εάν θέλουμε να υπολογίσουμε την απόσταση μεταξύ δύο //iterators// παρέχεται η συνάρτηση [[http://www.cplusplus.com/reference/iterator/distance/|distance]] η οποία υπολογίζει την απόσταση από το πρώτο όρισμα (//first//) έως το δεύτερο όρισμα (//last//) μετακινώντας τον δείκτη από //first// έως //last// με χρήση του τελεστή ''++''. Η συνάρτηση υποθέτει ότι το //first// βρίσκεται πριν από το //last//. Δείτε το παρακάτω παράδειγμα χρήσης των συγκεκριμένων συναρτήσεων #include // std::cout #include // std::advance #include // std::list int main () { std::list mylist; for (int i=0; i<10; i++) mylist.push_back (i*10); for(std::list::const_iterator it = mylist.cbegin(); it!=mylist.cend(); ++it) std::cout << *it << " "; std::cout << std::endl; std::list::iterator it = mylist.begin(); std::advance (it,5); std::cout << "The sixth element in mylist is: " << *it << '\n'; std::list::iterator it1 = mylist.begin(); std::advance(it1,2); std::list::iterator it2 = mylist.end(); std::advance(it2,-2); std::cout << "Distance between it1 and it2 is: " << distance(it1, it2) << std::endl; return 0; } ==== Κοινές συναρτήσεις για όλους τους containers για την επιστροφή των θέσεων αρχής και τέλους ==== H //STL// παρέχει τις //templated// συναρτήσεις [[http://www.cplusplus.com/reference/iterator/begin/|begin]] και [[http://www.cplusplus.com/reference/iterator/end|end]] για την εύρεση της αρχικής και της τελικής θέσης οποιουδήποτε //container//. Οι συναρτήσεις αυτές είναι διαφορετικές από τις συναρτήσεις μέλη //begin// και //end// που περιέχει κάθε //container//. Παράδειγμα χρήσης των συναρτήσεων αυτών δίνεται παρακάτω: #include // std::cout #include // std::vector, std::begin, std::end int main () { int foo[] = {10,20,30,40,50}; std::vector bar; // iterate foo: inserting into bar for (auto it = std::begin(foo); it!=std::end(foo); ++it) bar.push_back(*it); // iterate bar: print contents: std::cout << "bar contains:"; for (auto it = std::begin(bar); it!=std::end(bar); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; }