TEMPLATES STANDARD TEMPLATE LIBRARY ΟΝΤΟΚΕΝΤΡΙΚΟΣ ΠΡΟΓΡΑΜΜΑΤΙςΜΟς C++ 2010-2011 Evangelos Theodoridis Class Templates Templates Δημιουργία γενικευμένων κλάσεων και συναρτήσεων Function template η πρωτότυπη περιγραφή της συνάρτησης Template function μία συγκεκριμένη συνάρτηση από ένα function template
Class Templates (II) Class templates Παραμετροποίηση κλάσεων type-specific versions of generic classes Format: template <class T> class ClassName{ definition } Όχι μόνο "T", οποιοδήποτε αναγνωριστικό ClassName< type > myobject; πχ: Stack< double > doublestack; Class Templates (II) Template class functions Δηλώνονται κανονικά, ποηγείταιτο template<class T> Τα γενικευμένα δεδομένα αναφέρονται ως T Χρησιμοποιείται ο Binary scope resolution operator Παράδειγμα δήλωσης κλάσης : template<class T> MyClass<T>::MyClass(int size) { } myarray = new T[size]; Constructor δημιουργεί έναν πίνακα τύπου T
1 // Fig. 22.3: tstack1.h 2 // Class template Stack 3 #ifndef TSTACK1_H 4 #define TSTACK1_H 5 6 template< class T > 7 class Stack { 8 public: 9 Stack( int = 10 ); // default constructor (stack size 10) 10 ~Stack() { delete [] stackptr; } // destructor 11 bool push( const T& ); // push an element onto the stack 12 bool pop( T& ); // pop an element off the stack 13 private: 14 int size; // # of elements in the stack 15 int top; // location of the top element 16 T *stackptr; // pointer to the stack 17 18 bool isempty() const { return top == -1; } // utility 19 bool isfull() const { return top == size - 1; } // functions 20 }; 21 22 // Constructor with default size 10 23 template< class T > 24 Stack< T >::Stack( int s ) 25 { 26 size = s > 0? s : 10; 27 top = -1; // Stack is initially empty 28 stackptr = new T[ size ]; // allocate space for elements 29 } 2000 C Prentice Hall. Inc. All rights reserved 30 31 // Push an element onto the stack 32 // return 1 if successful, 0 otherwise 33 template< class T > 34 bool Stack< T >::push( const T &pushvalue ) 35 { 36 if (!isfull() ) { 37 stackptr[ ++top ] = pushvalue; // place item in Stack 38 return true; // push successful 39 } 40 return false; // push unsuccessful 41 } 42 43 // Pop an element off the stack 44 template< class T > 45 bool Stack< T >::pop( T &popvalue ) 46 { 47 if (!isempty() ) { 48 popvalue = stackptr[ top-- ]; // remove item from Stack 49 return true; // pop successful 50 } 51 return false; // pop unsuccessful 52 } 53 54 #endif 2000 C Prentice Hall. Inc. All rights reserved
55 // Fig. 22.3: fig22_03.cpp 56 // Test driver for Stack template 57 #include <iostream> 58 59 using std::cout; 60 using std::cin; 61 using std::endl; 62 63 #include "tstack1.h" 64 65 int main() 66 { 67 Stack< double > doublestack( 5 ); 68 double f = 1.1; 69 cout << "Pushing elements onto doublestack\n"; 70 71 while ( doublestack.push( f ) ) { // success true returned 72 cout << f << ' '; 73 f += 1.1; 74 } 75 76 cout << "\nstack is full. Cannot push " << f 77 << "\n\npopping elements from doublestack\n"; 78 79 while ( doublestack.pop( f ) ) // success true returned 2000 C Prentice Hall. Inc. All rights reserved 80 cout << f cout << ' '; << f << ' '; 81 82 82 cout << cout "\nstack << is empty. "\nstack Cannot pop\n"; is empty. 83 84 Cannot 83 pop\n"; Stack< int > intstack; 84 Stack< int > intstack; 85 i = 1; 85 int i = 1; 86 cout << "\npushing elements 86 cout << "\npushing elements onto intstack\n"; 87 88 onto 87 intstack\n"; while ( intstack.push( i ) ) 89 { // success cout true << returned i << ' '; 90 89 cout << i ++i; << ' '; 91 90 ++i; } 92 91 } 93 92 cout << "\nstack is full. Cannot 94 93 cout << "\nstack push " << is full. << "\n\npopping Cannot push " << i i elements 95 94 << "\n\npopping elements from intstack\n"; 96 while from intstack\n"; 95 ( intstack.pop( i ) ) // 97 96 success while ( intstack.pop( true cout returned << i ) i ) << // success ' '; true returned 98 97 cout << i << ' '; 99 cout << "\nstack is empty. 98 100 Cannot pop\n"; return 0; 99 cout << "\nstack is empty. Cannot pop\n"; 101 } 88 while ( intstack.push( i ) ) { // success true returned 100 return 0; 101 } 2000 C Prentice Hall. Inc. All rights reserved
Έξοδος Pushing elements onto doublestack 1.1 2.2 3.3 4.4 5.5 Stack is full. Cannot push 6.6 Popping elements from doublestack 5.5 4.4 3.3 2.2 1.1 Stack is empty. Cannot pop Pushing elements onto intstack 1 2 3 4 5 6 7 8 9 10 Stack is full. Cannot push 11 Popping elements from intstack 10 9 8 7 6 5 4 3 2 1 Stack is empty. Cannot pop Class Templates and Non-type Parameters Eίναιδυνατή η χρήση μη non-type parameters στα templates Default argument Χρησιμοποιείται ως const Παράδειγμα: template< class T, int elements > Stack< double, 100 > mostrecentsalesfigures; Stack< double, 100> class definition: T stackholder[ elements ]; //array to hold stack Δημιουργεί πίνακα την ώρα της μεταγλώττισης και όχι την ώρα της εκτέλεσης
Class Templates and Non-type Parameters (II) Υπέρβαση κλάσεων Για μία template class Array, δήλωσε μία κλάση Array<myCreatedType> Η νέα κλάση overrides την αρχική κλάση με το mycreatedtype Το αρχικό template παραμένει για unoverriden types Templates and Inheritance Ένα class template µπορεί να προκύψει από ένα άλλο template class Ένα class templateµπορείναπροκύψειαπό nontemplate class Ένα template class µπορεί να προκύψει από class template Ένα non-template class µπορεί να προκύψει από ένα class template
Templates and friends Friendships επιτρέπονται μεταξύ ενός class template και Global function Member function μίας άλλης κλάσης Entire class friend functions Inside definition of class template X: friend void f1(); f1() a friend of all template classes friend void f2( X< T > & ); f2( X< int > & ) is a friend of X< int > only. The same applies for float, double, etc. friend void A::f3(); Member function f3 of class A is a friend of all template classes Templates and friends friend void C< T >::f4( X< T > & ); C<float>::f4( X< float> & ) is a friend of class X<float> only friend classes friend class Y; Every member function of Y a friend with every template class made from X friend class Z<T>; Class Z<float> a friend of class X<float>, etc.
Templates and static Members Non-template class static data members shared between all objects Template classes Each class (int, float, etc.) has its own copy of static data members static variables initialized at file scope Each template class gets its own copy of static member functions Standard Template Library Η standard template library (STL) αποτελείται Containers Algorithms Iterators Έναςcontainer ένας τρόπος οργάνωσης μίας συλλογής αντικειμένων στην μνήμη Algorithms στην STLείναι διαδικασίας που εφαρμόζονται στους containers για να επεξεργαστούν τα δεδομένα τους, πχ. Αναζήτηση αντικειμένου Iterators είναι μία γενίκευση των δεικτών (pointers), δείχνουν αντικείμενα μέσα σε έναν container
Containers, Iterators, Algorithms Οι αλγόριθµοι χρησιµοποιούν iterators για να αλληλεπιδράσουν Με Αντικείµενα των containers Container Iterator Objects Iterator Algorithm Algorithm Iterator Iterator Container Algorithm Containers Ένας containerείναι ένας τρόπος να αποθηκεύσουμε δεδομένα, είτε βασικούς τύπους είτε αντικείμενα κλάσεων. Η STL παρέχει διάφορους βασικούς τύπους containers <vector> : one-dimensional array <list> : double linked list <deque> : double-ended queue <queue> : queue <stack> : stack <set> : set <map> : associative array
Sequence Containers Ένας sequence container αποθηκεύει ένα σύνολο αντικειμένων στην ακολουθία,με άλλα λόγια κάθε αντικείμενο (εκτός από το πρώτο και το τελευταίο) ακολουθείται από ένα συγκεκριμένο αντικείμενο: <vector>, <list> and <deque> Οι κλασικοί C++ πίνακες με σταθερό μέγεθος δεν μεταβάλλονται κατά την εκτέλεση έχουν το πλεονέκτημα της τυχαίας προσπέλασης <vector>είναι επεκτάσιμος τύπος αλλά οι εισαγωγές/διαγραφές στο μέσω είναι αργά. Sequence Containers <list> είναι διπλά διασυνδεδεμένη λίστα και είναι γρήγορη η εισαγωγή/διαγραφή αλλά αργή η προσπέλαση <deque> είναι ουρά δύο άκρων,δηλαδή εισάγει και διαγράφει στοιχεία από τα δύο άκρα stack +queue +list
Associative Containers Ένα associative container είναι non-sequential και χρησιμοποιεί ένα κλειδί (key) για την προσπέλαση των αντικειμένων. Τα κλειδιάείναι αριθμοίή συμβολοσειρέςχρησιμοποιούνται από τον container για να διαχειριστεί τα αντικείμενα με μία σειρά,πχ λεξικό. Associative Containers Ένα <set> αποθηκεύει ένα αριθμό αντικειμένων που περιέχουν κλειδιά. Τα κλειδιά είναι τα χαρακτηριστικά που χρησιμοποιούνται για την διάταξη των στοιχείων. Πχ. Ένα set μπορεί να αποθηκεύει αντικείμενα της κλάσης Personπου διατάσσονται αλφαβητικά ανάλογα με το όνομα τους Ένα <map>αποθηκεύει ζευγάρια αντικειμένων : ένα κλειδίκαι μία συσχετιζόμενη τιμή. Ένα <map> είναι αντίστοιχο με έναν πίνακα αλλά αντί για αριθμούς δείκτες μπορούν να χρησιμοποιηθούν οποιοδήποτε τύπου αντικείμενα <set> και <map>επιτρέπουν ένα κλειδί για κάθε τιμή ενώ τα <multiset> και <multimap> επιτρέπουν πολλές εμφανίσεις
Vector Container v.pop_back(); 12 7 9 21 13 int array[5] = {12, 7, 9, 21, 13 }; vector<int> v(array,array+5); v.push_back(15); 12 7 9 21 12 7 9 21 15 0 1 2 3 4 12 7 9 21 15 v.begin(); v[3] Vector Container #include <vector> #include <iostream> vector<int> v(3); // create a vector of ints of size 3 v[0]=23; v[1]=12; v[2]=9; // vector full v.push_back(17); // put a new value at the end of array for (int i=0; i<v.size(); i++) // member function size() of vector cout << v[i] << ; // random access to i-th element cout << endl;
Vector Container #include <vector> #include <iostream> int arr[] = { 12, 3, 17, 8 }; // standard C array vector<int> v(arr, arr+4); // initialize vector with C array while (! v.empty()) // until vector is empty { cout << v.back() << ; // output last element of vector v.pop_back(); // delete the last element } cout << endl; Constructors for Vector Ένας vectorμπορεί να αρχικοποιηθεί δίνοντας το μέγεθος του και τον τύπο του αντικειμένου ( prototype) ή άλλον vector. vector<date> x(1000); // creates vector of size 1000, // requires default constructor for Date vector<date> dates(10,date(17,12,1999)); // initializes // all elements with 17.12.1999 vector<date> y(x); // initializes vector y with vector x
Iterators Iterators είναι pointer-like αντικείμενα που χρησιμοποιούνται για την προσπέλαση αντικειμένων σε έναν container. Χρησιμοποιούνται για να κινούνται σειριακά από αντικείμενο σε αντικείμενο iterating through container. vector<int> array_ 17 4 23 12 size_ 4 vector<int>::iterator iterator class vector<int> vector<int>::iterator Iterators Οι member functions begin() και end() επιστρέφουν έναν iterator στο πρώτο και τελευταίο στοιχείο του container vector<int> v array_ 17 size_ 4 4 23 12 v.begin() v.end()
Iterators Μπορούμε να έχουμε πολλαπλούς iterators ταυτόχρονα vector<int> v array_ 17 4 23 12 size_ 4 i1 i2 i3 Iterators #include <vector> #include <iostream> int arr[] = { 12, 3, 17, 8 }; // standard C array vector<int> v(arr, arr+4); // initialize vector with C array vector<int>::iterator iter=v.begin(); // iterator for class vector // define iterator for vector and point it to first element of v cout << first element of v= << *iter; // de-reference iter iter++; // move iterator to next element iter=v.end()-1; // move iterator to last element
Iterators int max(vector<int>::iterator start, vector<int>::iterator end) { int m=*start; while(start!= stop) { if (*start > m) m=*start; ++start; } return m; } cout << max of v = << max(v.begin(),v.end()); Iterators #include <vector> #include <iostream> int arr[] = { 12, 3, 17, 8 }; // standard C array vector<int> v(arr, arr+4); // initialize vector with C array for (vector<int>::iterator i=v.begin(); i!=v.end(); i++) // initialize i with pointer to first element of v // i++ increment iterator, move iterator to next element { } cout << *i << ; // de-referencing iterator returns the cout << endl; // value of the element the iterator points at
Κατηγορίες Iterator Δεν είναι δυνατό κάθε iterator να χρησιμοποιηθεί με κάθε container πχ. η κλάση λίστα δεν δίνειrandom access iterator Κάθε αλγόριθμος απαιτεί έναν iterator με κάποιες συγκεκριμένες ιδιότητες πχ. Να χρησιμοποιεί το [] τελεστή για random access Οι Iterators χωρίζονται σε 5 κατηγορίες όπου κάθε κατηγορία εμπεριέχει την κατώτερη της πχ.ένας αλγόριθμος με forward iteratorθα δουλεύει bidirectional iterator και random access iterator input output forward bidirectional random access For_Each() Algorithm #include <vector> #include <algorithm> #include <iostream> void show(int n) { cout << n << ; } int arr[] = { 12, 3, 17, 8 }; // standard C array vector<int> v(arr, arr+4); // initialize vector with C array for_each (v.begin(), v.end(), show); // apply function show // to each element of vector v
Find() Algorithm #include <vector> #include <algorithm> #include <iostream> int key; int arr[] = { 12, 3, 17, 8, 34, 56, 9 }; // standard C array vector<int> v(arr, arr+7); // initialize vector with C array vector<int>::iterator iter; cout << enter value : ; cin >> key; iter=find(v.begin(),v.end(),key); // finds integer key in v if (iter!= v.end()) // found the element cout << Element << key << found << endl; else cout << Element << key << not in vector v << endl; Find_If() Algorithm #include <vector> #include <algorithm> #include <iostream> Bool mytest(int n) { return (n>21) && (n <36); }; int arr[] = { 12, 3, 17, 8, 34, 56, 9 }; // standard C array vector<int> v(arr, arr+7); // initialize vector with C array vector<int>::iterator iter; iter=find_if(v.begin(),v.end(),mytest); // finds element in v for which mytest is true if (iter!= v.end()) // found the element cout << found << *iter << endl; else cout << not found << endl;
Count_If() Algorithm #include <vector> #include <algorithm> #include <iostream> Bool mytest(int n) { return (n>14) && (n <36); }; int arr[] = { 12, 3, 17, 8, 34, 56, 9 }; // standard C array vector<int> v(arr, arr+7); // initialize vector with C array int n=count_if(v.begin(),v.end(),mytest); // counts element in v for which mytest is true cout << found << n << elements << endl; List Container Ένας STL list container είναι μία διπλά διασυνδεδεμένη λίστα: κάθε αντικείμενο δείχνει στον προηγούμενο και στον επόμενο Είναι δυνατό να προσθέσουμε/αφαιρέσουμε στοιχεία από τα δύο άκρα της λίστας Δεν επιτρέπει random access αλλά μπορούμε να εισάγουμε/διαγράψουμε από την μέση καθώς και να συνενώσουμε και να διατάξουμε
List Container int array[5] = {12, 7, 9, 21, 13 }; 12 7 9 21 13 list<int> li(array,array+5); li.pop_back(); li.push_back(15); 12 7 9 21 li.pop_front(); 12 7 9 21 15 li.push_front(8); 7 9 21 8 12 7 9 21 15 li.insert() 7 12 17 21 23 Insert Iterators Η χρήση του copy algorithm διαγράφει τα υπάρχοντα στοιχεία #include <list> int arr1[]= { 1, 3, 5, 7, 9 }; int arr2[]= { 2, 4, 6, 8, 10 }; list<int> l1(arr1, arr1+5); // initialize l1 with arr1 list<int> l2(arr2, arr2+5); // initialize l2 with arr2 copy(l1.begin(), l1.end(), l2.begin()); // copy contents of l1 to l2 overwriting the elements in l2 // l2 = { 1, 3, 5, 7, 9 }
Insert Iterators Οι operators διαφοροποιούν τη συμπεριφορά του copy algorithm back_inserter : εισάγει νέο στοιχείο στο τέλος front_inserter : εισάγει νέο στοιχείο στην αρχή inserter : εισάγει νέο στοιχείο σε θέση #include <list> int arr1[]= { 1, 3, 5, 7, 9 }; int arr2[]= { 2, 4, 6, 8, 10 }; list<int> l1(arr1, arr1+5); // initialize l1 with arr1 list<int> l2(arr2, arr2+5); // initialize l2 with arr2 copy(l1.begin(), l1.end(), back_inserter(l2)); // use back_inserter // adds contents of l1 to the end of l2 = { 2, 4, 6, 8, 10, 1, 3, 5, 7, 9 } copy(l1.begin(), l1.end(), front_inserter(l2)); // use front_inserter // adds contents of l1 to the front of l2 = { 9, 7, 5, 3, 1, 2, 4, 6, 8, 10 } copy(l1.begin(), l1.end, inserter(l2,l2.begin()); // adds contents of l1 at the old beginning of l2 = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 } Sort & Merge Sort και merge #include <list> int arr1[]= { 6, 4, 9, 1, 7 }; int arr2[]= { 4, 2, 1, 3, 8 }; list<int> l1(arr1, arr1+5); // initialize l1 with arr1 list<int> l2(arr2, arr2+5); // initialize l2 with arr2 l1.sort(); // l1 = {1, 4, 6, 7, 9} l2.sort(); // l2= {1, 2, 3, 4, 8 } l1.merge(l2); // merges l2 into l1 // l1 = { 1, 1, 2, 3, 4, 4, 6, 7, 8, 9}, l2= {}
Functions Objects sort, merge, accumulate μπορούν να πάρουν σαν ορίσματα ένα function object Το function object είναι ένα αντικείμενο μιας template classπου έχει ένα single member function : the overloaded operator () Είναι δυνατό να χρησιμοποιηθούν user-written functions στη θέση των pre-defined function objects #include <list> #include <functional> int arr1[]= { 6, 4, 9, 1, 7 }; list<int> l1(arr1, arr1+5); // initialize l1 with arr1 l1.sort(greater<int>()); // uses function object greater<int> // for sorting in reverse order l1 = { 9, 7, 6, 4, 1 } Function Objects Ο algorithm accumulate συναθροίζει δεδομένα πάνω από τα στοιχεία πχ sum of elements #include <list> #include <functional> #include <numeric> int arr1[]= { 6, 4, 9, 1, 7 }; list<int> l1(arr1, arr1+5); // initialize l1 with arr1 int sum = accumulate(l1.begin(), l1.end(), 0, plus<int>()); int sum = accumulate(l1.begin(), l1.end(),0); // equivalent int fac = accumulate(l1.begin(), l1.end(), 0, times<int>());
User Defined Function Objects class squared _sum // user-defined function object { public: int operator()(int n1, int n2) { return n1+n2*n2; } }; int sq = accumulate(l1.begin(), l1.end(), 0, squared_sum() ); // computes the sum of squares User Defined Function Objects template <class T> class squared _sum // user-defined function object { }; public: T operator()(t n1, T n2) { return n1+n2*n2; } vector<complex> vc; complex sum_vc; vc.push_back(complex(2,3)); vc.push_back(complex(1,5)); vc.push_back(complex(-2,4)); sum_vc = accumulate(vc.begin(), vc.end(), complex(0,0), squared_sum<complex>() ); // computes the sum of squares of a vector of complex numbers
Associative Containers Σε ένα associative container τα αντικείμενα δεν σχηματίζουν ακολουθία αλλά δενδρικές δομές ή hash table. Τα προτερήματα τους είναι η ταχύτητα αναζήτησης (δυαδική κλπ) Η αναζήτηση γίνεται με βάση το κλειδί πού είναι είτε αριθμός είτε συμβολοσειρά Η value είναι ένα χαρακτηριστικό των objects στο container Η STL έχει δύο βασικά associative containers sets and multisets maps and multimaps Sets and Multisets #include <set> string names[] = { Ole, Hedvig, Juan, Lars, Guido }; set<string, less<string> > nameset(names,names+5); // create a set of names in which elements are alphabetically // ordered string is the key and the object itself nameset.insert( Patric ); // inserts more names nameset.insert( Maria ); nameset.erase( Juan ); // removes an element set<string, less<string> >::iterator iter; // set iterator string searchname; cin >> searchname; iter=nameset.find(searchname); // find matching name in set if (iter == nameset.end()) // check if iterator points to end of set cout << searchname << not in set! <<endl; else cout << searchname << is in set! <<endl;
Set and Multisets string names[] = { Ole, Hedvig, Juan, Lars, Guido, Patric, Maria, Ann }; set<string, less<string> > nameset(names,names+7); set<string, less<string> >::iterator iter; // set iterator iter=nameset.lower_bound( K ); // set iterator to lower start value K while (iter!= nameset.upper_bound( Q )) cout << *iter++ << endl; // displays Lars, Maria, Ole, Patric Maps and Multimaps Ένα map αποθηκεύει pairs <key, value> ενός key object και ενός associated value object. Το key object περιέχει ένα key με το οποίο αναζητούμε και το value object περιέχει τις πληροφορίες Το key μπορεί να είναι string, πχ όνομα, ΑΣΤ, ή αριθμός
Maps and Multimaps #include <map> string names[]= { Ole, Hedvig, Juan, Lars, Guido, Patric, Maria, Ann }; int numbers[]= {75643, 83268, 97353, 87353, 19988, 76455, 77443,12221}; map<string, int, less<string> > phonebook; map<string, int, less<string> >::iterator iter; for (int j=0; j<8; j++) phonebook[names[j]]=numbers[j]; // initialize map phonebook for (iter = phonebook.begin(); iter!=phonebook.end(); iter++) cout << (*iter).first << : << (*iter).second << endl; cout << Lars phone number is << phonebook[ Lars ] << endl; Person Class class person { } private: string lastname; string firstname; long phonenumber; public: person(string lana, string fina, long pho) : lastname(lana), firstname(fina), phonenumber(pho) {} bool operator<(const person& p); bool operator==(const person& p);
Maps & Multimaps person p1( Neuville, Oliver, 5103452348); person p2( Kirsten, Ulf, 5102782837); person p3( Larssen, Henrik, 8904892921); multiset<person, less<person>> persset; multiset<person, less<person>>::iterator iter; persset.insert(p1); persset.insert(p2); persset.insert(p3);