User Tools

Site Tools


cpp:stl:containers_commons

This is an old revision of the document!


Κοινά χαρακτηριστικά για όλους τους Containers

Εισαγωγή των στοιχείων μέσα σε ένα Container

Η εισαγωγή στοιχείων σε οποιονδήποτε container γίνεται δημιουργώντας ένα αντίγραφο του αντικειμένου προς εισαγωγή και αποθήκευση του νέου αντικείμενου στον container. Οι τρόποι με τους οποίους δημιουργείται το αντίγραφο περιγράφονται παρακάτω.

Μέθοδοι insert και emplace

Οι μέθοδοι insert και emplace εισάγουν στοιχεία σε ένα container, αλλά το κάνουν με διαφορετικό τρόπο. Η μέθοδος insert λαμβάνει ως παράμετρο το προς εισαγωγή αντικείμενο και με την βοήθεια του copy-constructor του αντικείμένου αντιγράφει το αντικείμενο μέσα στην δομή. Η μέθοδος συνεπάγεται α) την δημιουργία του αντικειμένου, β) την αντιγραφή του κατά την κλήση της insert και την αντιγραφή του στον container.

Αντίθετα, η μέθοδος emplace λαμβάνει μεταβλητό αριθμό παραμέτρων και αποσκοπεί στην δημιουργία του προς ένθεση αντικειμένου μία φορά εντός του container, καλώντας τον κατάλληλο κατασκευαστή για το αντικείμενο. Η επιλογή του κατασκευαστή γίνεται με βάση τον αριθμό και των τύπο των ορισμάτων που λαμβάνει η συνάρτηση emplace.

Για παράδειγμα, για την ένθεση στοιχείων της κλάσης Student μέσα σε ένα container list τα στοιχεία θα αντιγραφούν εντός του list στο τέλος της λίστας (όλες οι μέθοδοι insert ή emplace στον παρακάτω κώδικα - είτε βρίσκονται σε σχόλια, είτε όχι - εισάγουν το αντικείμενο στο τέλος της λίστας):

student_list.cpp
#include <iostream>     // std::cout
#include <algorithm>    // std::copy
#include <list>         // std::list
#include <array>        // 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<Student> mylist;
  for(int i=0; i<4; i++) {
    mylist.push_back(students[i]);
    //mylist.insert(mylist.end(),students[i]);                                   // equivalent with push_back
    //mylist.emplace_back(students[i].getName(), students[i].getAEM());
    //mylist.emplace(mylist.end(), students[i].getName(), students[i].getAEM()); // equivalent with emplace_back
  }
 
  std::cerr << "-------------------------\n";
  std::cerr << "mylist contains:";
  for (std::list<Student>::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 των οποίων μπορούμε να προσπελάσουμε οποιαδήποτε θέση με χρονικό κόστος Ο(1). Οι containers αυτοί είναι οι array, vector και deque. Μέσω του τελεστή αυτού και με τη βοήθεια του τελεστή ανάθεσης = για το αντικείμενο που εισάγεται, μπορούμε να λάβουμε ή να θέσουμε την τιμή μιας θέσης του container.

Στο παρακάτω παράδειγμα ανατίθεται σε ένα container τύπου array περιεχόμενα μέσω του τελεστή [ ]. Μεταγλωττίζοντας και εκτελώντας το πρόγραμμα θα παρατηρήσετε ότι κατά την ανάθεση καλείται ο τελεστής = (operator=) της κλάσης Student.

student_array.cpp
#include <iostream>     // std::cout
#include <algorithm>    // std::copy
#include <array>       // std::array
#include <array>        // 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<Student,2> 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<Student,2>::iterator it = myarray.begin(); it!=myarray.end(); ++it)
    std::cerr << ' ' << *it;
  std::cerr << std::endl;
  std::cerr << "-------------------------\n";
 
  return 0;
} 

Προσδιορισμός του εύρους των στοιχείων ενός Container μέσω iterator

Όταν στην STL προσδιορίζεται ένα εύρος στοιχείων εντός ενός container μεταξύ των υποτιθέμενων θέσεων start και stop ( οι θέσεις αυτές προσδιορίζονται πάντοτε με τη βοήθεια iterators), το διάστημα το οποίο υπολογίζεται είναι από start (συμπεριλαμβανομένου) έως και stop (μη συμπεριλαμβανομένου), ισοδύναμα: [start, stop). Η παρακάτω εικόνα περιγράφει το διάστημα μεταξύ των θέσεων start=1 και stop=6 ενός πίνακα. Το προσδιορισθέν διάστημα είναι από start έως και stop-1, δηλαδή από 1 έως και 5 (με πράσινο χρώμα).

Δείτε το παρακάτω παράδειγμα, όπου αντιγράφονται τα περιεχόμενα του πίνακα array αρχικά στον πίνακα myarray και στη συνέχεια ένα μέρος από αυτά στη λιστα mylist.

range_using_iterator.cpp
#include <iostream>     // std::cout
#include <list>         // std::list
#include <array>        // std::array
#define SIZE 10
 
int main () {
  int array[] = { 1,2,3,4,5,6,7,8,9,10 };
 
  std::array<int,SIZE> myarray;
  for(int i=0; i<SIZE; i++)
    myarray[i] = array[i];
 
  std::cerr << "myarray contains:";
  for (std::array<int,SIZE>::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<int> mylist;
  mylist.assign(myarray.cbegin() + 1, myarray.cbegin() + 4);   // copy array positions 1, 2 and 3
  std::cerr << "mylist contains:";
  for (std::list<int>::iterator it = mylist.begin(); it!=mylist.end(); ++it)
    std::cerr << ' ' << *it;
  std::cerr << std::endl;                                 // mylist: 2 3 4
 
  return 0;
}

Η μέθοδος assign δημιουργεί ένα αντίγραφο των περιεχομένων ξεκινώντας από το πρώτο όρισμα (συμπεριλαμβανομένου) έως και το δεύτερο όρισμα (μη συμπεριλαμβανομένου). Στο παραπάνω παράδειγμα ξεκινά από τη θέση 1 του πίνακα (myarray.cbegin()+1) έως και τη θέση 3 (myarray.cbegin()+3). Η θέση myarray.cbegin()+4 που είναι το 2ο όρισμα δεν περιλαμβάνεται στο διάστημα.

Όπως θα δούμε στη συνέχεια, οι iterators της STL διατρέχουν οποιαδήποτε δομή από τη θέση begin() (δείκτης στην πρώτη θέση περιεχομένων του container) έως end() (δείκτης αμέσως μετά την τελευταία θέση περιεχομένων του container).

cpp/stl/containers_commons.1623134346.txt.gz · Last modified: 2021/06/08 05:39 (external edit)