====== Συμπληρωματικοί τρόποι πρόσβασης των στοιχείων των STL Containers ====== Η εισαγωγή στοιχείων σε οποιονδήποτε //container// γίνεται δημιουργώντας ένα αντίγραφο του αντικειμένου προς εισαγωγή και αποθήκευση του νέου αντικείμενου στον //container//. Οι τρόποι με τους οποίους δημιουργείται το αντίγραφο περιγράφονται παρακάτω. ===== Μέθοδοι insert και emplace ===== Οι μέθοδοι //insert// και //emplace// εισάγουν στοιχεία σε ένα //container//, αλλά το κάνουν με διαφορετικό τρόπο. Η μέθοδος //insert// λαμβάνει ως παράμετρο το προς εισαγωγή αντικείμενο και με την βοήθεια του copy-constructor του αντικείμένου αντιγράφει το αντικείμενο μέσα στην δομή. Η μέθοδος συνεπάγεται α) την δημιουργία του αντικειμένου, β) την αντιγραφή του κατά την κλήση της insert και την αντιγραφή του στον container. Αντίθετα, η μέθοδος //emplace// λαμβάνει μεταβλητό αριθμό παραμέτρων και αποσκοπεί στην δημιουργία του προς ένθεση αντικειμένου μία φορά εντός του //container//, καλώντας τον κατάλληλο κατασκευαστή για το αντικείμενο. Η επιλογή του κατασκευαστή γίνεται με βάση τον αριθμό και των τύπο των ορισμάτων που λαμβάνει η συνάρτηση //emplace//. Για παράδειγμα, για την ένθεση στοιχείων της κλάσης [[https://courses.e-ce.uth.gr/ECE326/doku.php?do=export_code&id=cpp:templates&codeblock=0|Student]] μέσα σε ένα //container list// τα στοιχεία θα αντιγραφούν εντός του //list// στο τέλος της λίστας (όλες οι μέθοδοι //insert// ή //emplace// στον παρακάτω κώδικα - είτε βρίσκονται σε σχόλια, είτε όχι - εισάγουν το αντικείμενο στο τέλος της λίστας): #include // std::cout #include // std::copy #include // std::list #include // std::array #include "Student.hpp" int main () { Student students[] = { Student("Peter_Pan", 1234), Student("Tinker_Bell", 1235), Student("Mickey Mouse", 1236), Student("Minnie Mouse", 1237) }; std::cerr << "----- Init list -----" << std::endl; std::list mylist; for(int i=0; i<4; i++) { mylist.insert(mylist.end(),students[i]); // equivalent with push_back //mylist.emplace(mylist.end(), students[i].getName(), students[i].getAEM()); // equivalent with emplace_back } std::cerr << "-------------------------\n"; std::cerr << "mylist contains:"; for (std::list::iterator it = mylist.begin(); it!=mylist.end(); ++it) std::cerr << ' ' << *it; std::cerr << std::endl; std::cerr << "-------------------------\n"; return 0; } Κατεβάστε, μεταγλωττίστε και εκτελέστε το παραπάνω πρόγραμμα. Ποιος κατασκευαστής καλείται κατά την κατασκευή των αντικείμενων μέσω της συνάρτησης //push_back//. Δοκιμάστε να αντικαταστήσετε τη //push_back// με την //emplace_back// και δοκιμάστε να μεταγλωττίσετε-εκτελέσετε ξανά. Τοποθετήστε κατάλληλα //debug// μηνύματα στους κατασκευαστές της κλάσης //Student// και παρατηρήστε τις διαφορές στις εκτυπώσεις στις δύο περιπτώσεις. Κατά την εισαγωγή ενός στοιχείου μέσω των συναρτήσεων //insert//, //insert_back//, //insert_front// //emplace//, //emplace_back//, //emplace_front// δημιουργείται πάντα ένα νέο αντικείμενο στον //container// που αποτελεί αντίγραφο του προς εισαγωγή αντικειμένου. Η δημιουργία του αντικειμένου γίνεται είτε * μέσω του copy-constructor, όταν καλούνται οι μέθοδοι //insert//, //insert_front// και //insert_back// ή * μέσω εκείνου του κατασκευαστή που λαμβάνει ως παραμέτρους τους τύπους των ορισμάτων που περνιούνται στις μεθόδους //emplace// ή //emplace_front// ή //emplace_back//. Οι μέθοδοι //emplace// δημιουργούν ένα αντικείμενο χρησιμοποιώντας τον κατασκευαστή της κλάσης αντί για τη χρήση του //copy-constructor//. Το δυνητικό πλεονέκτημα των μεθόδων αυτών είναι ότι δεν κατασκευάζεται ένα αντικείμενο που στη συνέχεια αντιγράφεται, αλλά δημιουργείται μόνο ένα αντικείμενο εξ' αρχής. Στο προηγούμενο παράδειγμα, τα αντικείμενα κατασκευάζονται με τη βοήθεια του κατασκευαστή ''Student(const char *name, int aem)''. ===== Χρήση του τελεστή [ ] ===== === Χρήση σε sequence_containers === Ο τελεστής %%[ ]%% διατίθεται σε //sequence containers// των οποίων μπορούμε να προσπελάσουμε οποιαδήποτε θέση με χρονικό κόστος **Ο(1)**. Οι //containers// αυτοί είναι οι **//array, vector//** και **//deque//**. Μέσω του τελεστή αυτού και με τη βοήθεια του τελεστή ανάθεσης %%=%% για το αντικείμενο που εισάγεται, μπορούμε να λάβουμε ή να θέσουμε την τιμή μιας θέσης του //container//. Στο παρακάτω παράδειγμα ανατίθεται σε ένα //container// τύπου //array// περιεχόμενα μέσω του τελεστή %%[ ]%%. Μεταγλωττίζοντας και εκτελώντας το πρόγραμμα θα παρατηρήσετε ότι κατά την ανάθεση καλείται ο τελεστής %%=%% (//operator=//) της κλάσης //Student//. #include // std::cout #include // std::copy #include // std::array #include // std::array #include "Student.hpp" int main () { Student students[] = { Student("Peter_Pan", 1234), Student("Tinker_Bell", 1235) }; std::cerr << "----- Init array -----" << std::endl; std::array myarray; for(int i=0; i<2; i++) { myarray[i] = students[i]; // we use operator= here } std::cerr << "-------------------------\n"; std::cerr << "myarray contains:"; for (std::array::iterator it = myarray.begin(); it!=myarray.end(); ++it) std::cerr << ' ' << *it; std::cerr << std::endl; std::cerr << "-------------------------\n"; return 0; } === Χρήση σε map και unordered_map === Ο τελεστής %%[ ]%% διατίθεται σε containers τύπου map για την απεικόνιση κλειδιών σε τιμές. Όπως και παραπάνω μπορεί τόσο να ανακτήσει όσο και να καταχωρήσει μία τιμή σε ένα κλειδί. Παράδειγμα προγράμματος όπου χρησιμοποιείται ο συγκεκριμένος τελεστής δίνεται παρακάτω: // accessing mapped values #include #include #include int main () { std::map mymap; mymap['a']="an element"; mymap['b']="another element"; mymap['c']=mymap['b']; std::cout << "mymap['a'] is " << mymap['a'] << '\n'; std::cout << "mymap['b'] is " << mymap['b'] << '\n'; std::cout << "mymap['c'] is " << mymap['c'] << '\n'; std::cout << "mymap['d'] is " << mymap['d'] << '\n'; // creates mymap['d'] with an empty // string as assosiative value. std::cout << "mymap now contains " << mymap.size() << " elements.\n"; return 0; } ===== Προσδιορισμός του εύρους των στοιχείων ενός Container μέσω iterator ===== Όταν στην STL προσδιορίζεται ένα εύρος στοιχείων εντός ενός //container// μεταξύ των υποτιθέμενων θέσεων //**start**// και //**stop**// ( οι θέσεις αυτές προσδιορίζονται πάντοτε με τη βοήθεια //iterators//), το διάστημα το οποίο υπολογίζεται είναι από //**start**// (συμπεριλαμβανομένου) έως και //**stop**// (μη συμπεριλαμβανομένου), ισοδύναμα: **[start, stop)**. Η παρακάτω εικόνα περιγράφει το διάστημα μεταξύ των θέσεων //start=1// και //stop//=6 ενός πίνακα. Το προσδιορισθέν διάστημα είναι από //start// έως και //stop-1//, δηλαδή από 1 έως και 5 (με πράσινο χρώμα). {{ :cpp:stl:stl_range.png?400 |}} Δείτε το παρακάτω παράδειγμα, όπου αντιγράφονται τα περιεχόμενα του πίνακα //array// αρχικά στον πίνακα //myarray// και στη συνέχεια ένα μέρος από αυτά στη λιστα //mylist//. Για την αντιγραφή χρησιμοποιείται η συνάρτηση assign την οποία θα συναντήσουμε και στη συνέχεια. #include // std::cout #include // std::list #include // std::array #define SIZE 10 int main () { int array[] = { 1,2,3,4,5,6,7,8,9,10 }; std::array myarray; for(int i=0; i::iterator it = myarray.begin(); it!=myarray.end(); ++it) std::cerr << ' ' << *it; std::cerr << std::endl; // myarray: 1 2 3 4 5 6 7 8 9 10 std::list mylist; mylist.assign(myarray.cbegin() + 1, myarray.cbegin() + 4); // copy array positions 1, 2 and 3 std::cerr << "mylist contains:"; for (std::list::iterator it = mylist.begin(); it!=mylist.end(); ++it) std::cerr << ' ' << *it; std::cerr << std::endl; // mylist: 2 3 4 return 0; } Η συνάρτηση [[http://www.cplusplus.com/reference/list/list/assign/|assign]] δημιουργεί ένα αντίγραφο των περιεχομένων ξεκινώντας από το πρώτο όρισμα (συμπεριλαμβανομένου) έως και το δεύτερο όρισμα (μη συμπεριλαμβανομένου). Στο παραπάνω παράδειγμα ξεκινά από τη θέση **1** του πίνακα (**myarray.cbegin()+1**) έως και τη θέση **3** (**myarray.cbegin()+3**). Η θέση **myarray.cbegin()+4** που είναι το 2ο όρισμα δεν περιλαμβάνεται στο διάστημα. Η συνάρτηση [[http://www.cplusplus.com/reference/list/list/assign/|assign]] είναι διαθέσιμη μόνο για sequence_containers και όχι για associative ή unordered_associative containers (όπως //set,map,unordered_set,unordered_map//). Όπως θα δούμε στη συνέχεια, οι //iterators// της //STL// διατρέχουν οποιαδήποτε δομή από τη θέση //begin()// (δείκτης στην πρώτη θέση περιεχομένων του //container//) έως //end()// (δείκτης αμέσως μετά την τελευταία θέση περιεχομένων του //container//).