class Wektor private: Typ x; Typ y; Wektor(Typ a, Typ b) :x(a), y(b) ; Wektor<int> A(2, 5); Wektor<double> B(2.7, 9.7); Wektor<char> C('A', 'B'); 1 Definicja szablonu klasy. Korzystanie z szablonu. class X Typ a; Typ b; X(Typ,Typ); ; X<int> A(4, 77); A.metoda( ); X<double> AA(4.7, 5.9); AA.metoda( ); X<std::string> AAA("ola","ala"); AAA.metoda( ); X<Typ>::X(Typ aa,typ bb) : a(aa), b(bb) 4 77 4.7 5.9 ola ala 2 Konkretyzacja dla typów definiowane przez uŝytkownika. class Skarb int skarb; friend std::ostream& operator<<(std::ostream& ekran, const Skarb& co) return ekran<<"skarb = "<<co.skarb<<" "; ; X<Typ>::X(Typ aa,typ bb) : a(aa), b(bb) Skarb S, Q; X<Skarb> P(S, Q); P.metoda( ); 3 Specjalizacja metody szablonu. class X Typ a; Typ b; X(Typ,Typ); ; template<> void X<double>::metoda(); X<int> A(4, 77); A.metoda( ); X<double> AA(4.7, 5.9); AA.metoda( ); X<std::string> AAA("ola","ala"); AAA.metoda( ); X<Typ>::X(Typ aa,typ bb) : a(aa), b(bb) template<> void X<double>::metoda() std::cout<<a<<" & "<<b<<"\n"; 4 77 4.7 & 5.9 ola ala 4
Specjalizacja całego szablonu. Czasami dla pewnego typu nie moŝna uŝywać pełnej definicji szablonu klasy. W takiej sytuacji specjalizuje się cały szablon klasy. Przed jawną specjalizacją szablonu klasy musi być znana deklaracja ogólnego szablonu klasy. Definiując specjalizację szablonu klasy, musimy równieŝ zdefiniować wszystkie metody klasy. Ogólne definicje składowych szablonu klasy nigdy nie są uŝywane do tworzenia definicji składowych jawnej specjalizacji. W specjalizacji szablonu klasy moŝe wystąpić zupełnie inny zbiór składowych klasy niŝ w jej ogólnym szablonie. JeŜeli specjalizacja dotyczy całej klasy, to tylko przed jej definicją umieszczamy template< >. 5 Specjalizacja całego szablonu. class X Typ a, b; X(Typ,Typ); ; template<> class X<int> int a; X(int); ; X<int> A(4); A.metoda( ); X<double> AA(4.7, 5.9); AA.metoda( ); X<std::string> AAA("ola","ala"); AAA.metoda( ); X<Typ>::X(Typ aa,typ bb) : a(aa), b(bb) X<int>::X(int aa):a(aa) void X<int>::metoda() std::cout<<"( "<<a<<" )\n"; ( 4 ) 4.7 5.9 ola ala 6 Dziedziczenie a szablony klas. Szablon klasy dziedziczy klasę nieszablonową Klasa dziedziczy klasę szablonową void wypisz( ) cout<<"schowek "<<Typ(123.67)<<endl; ; class Sekret :public Schowek<int> void pisz( ) cout<<"sekret"<<endl; wypisz( ); ; Sekret S; Sekret 7 void wypisz( ) cout<<"schowek "<<endl<<""<<endl; ; template <class T> class Sekret :public Schowek void pisz( ) cout<<"sekret "<<T(345.98)<<endl; wypisz( ); ; Sekret<int> S; Sekret<double> SS; S Schowek Schowek 8
Szablon klasy dziedziczy klasę szablonową void wypisz( ) cout<<"schowek "<<Typ(123.67)<<endl<<""<<endl; ; template <class T> class Sekret :public Schowek<double> void pisz( ) cout<<"sekret "<<T(345.98)<<endl; wypisz( ); ; Szablon klasy dziedziczy szablon innej klasy void wypisz ( ) cout<<"schowek "<<Typ(123.67)<<endl<<""<<endl; ; template <class T> class Sekret :public Schowek<T> void pisz( ) cout<<"sekret "<<T(345.98)<<endl; wypisz( ); ; Sekret<int> S; Sekret<double> SS; S 9 Sekret<int> S; Sekret<double> SS; S 10 Szablon klasy dziedziczy szablon innej klasy void wypisz() cout<<"schowek "<<Typ(123.67)<<endl<<""<<endl; ; template <class T, class P> class Sekret :public Schowek<P> void pisz() cout<<"sekret "<<T(345.98)<<endl; wypisz(); ; Sekret<int, double> S; Sekret<double, int> SS; S Sekret<double, double> SSS; SS Sekret<int, int> SSSS; SSS 11 #include <iostream> int min(int a,int b); using namespace std; cout<<min(10,20)<<endl; cout<<min<int>(10,20)<<endl; cout<<min(10.9,20)<<endl; cout<<min<double>(10.9,20)<<endl; int min(int a,int b) cout<<"*"; return (a<b)? a:b; Co pojawi się na ekranie? *10 10 *10 10.9 12
#include <iostream> #include <algorithm> bool wieksze(int a,int b); using namespace std; int main() int A[5]=1,7,8,-9,2; cout<<*min_element(a,a+5)<<endl; sort(a,a+5); for(int i=0; i<5; i++) cout<<a[i]<<" "; cout<<endl; sort(a,a+3,&wieksze); for(int i=0; i<5; i++) cout<<a[i]<<" "; -9-9 1 2 7 8 2 1-9 7 8 STL Standard Template Library Umowa: STL standardowa biblioteka, która posługuje się iteratorami. Zatem do STL naleŝą: kolekcje (kontenery), algorytmy uogólnione oraz obiekty funkcyjne (funktory). Kolekcje - szablony klas Kolekcje dzielimy na: sekwencyjne np.: vector, list asocjacyjne np.: map, set. Algorytmy uogólnione szablony funkcji. Algorytmy uogólnione moŝemy podzielić na modyfikujące (np.: remove, sort) i nie modyfikujące (find, count). Obiekty funkcyjne obiekty klas, które zawierają co najmniej przeciąŝony operator wywołania funkcji ( ). UmoŜliwiają zastąpienie domyślnego znaczenia algorytmów uogólnionych. bool wieksze(int a,int b) return (a>b)? true:false; 13 Iterator jest obiektem, który porusza się w obrębie kolekcji innych obiektów. Udostępnia on za kaŝdym razem pojedynczy, zawarty w kolekcji, obiekt. Korzystając z iteratora moŝna przejść przez elementy całej kolekcji w taki sposób w jaki wskaźnik przebiega po elementach tablicy. 14 Kolekcje sekwencyjne i ich niektóre składowe: Typy składowe: value_type typ elementu iterator zachowuje się jak value_type* const_iterator zachowuje się jak const value_type* reverse_iterator przegląda kolekcję w odwrotnym porządku; jak value_type* const_reverse_iterator przegląda kolekcję w odwrotnym porządku; jak const value_type* reference zachowuje się jak value_type const_ reference zachowuje się jak const value_type Iteratory - inicjowanie begin( ) wskazuje na pierwszy element end( ) wskazuje na element pierwszy za ostatnim rbegin( ) wskazuje na pierwszy element w ciągu odwróconym rend( ) wskazuje na element pierwszy za ostatnim w ciągu odwróconym Dostęp do elementu w kolekcji A: A.front( ) pierwszy element A.back( ) ostatni element A[nr] indeksowanie, dostęp niekontrolowany (nie dla list) A.at(nr) dostęp kontrolowany (nie dla list) iteratory za pomocą * 15 Nadawanie wartości początkowych konstruktory. Destruktor. container ( ) pusta kolekcja container (n) n elementów z domyślną wartością container (n, x) n kopii x container (first, last) elementy z [first, last) container (X) konstruktor kopiujący ~ container ( ) zniszcz kolekcje i wszystkie jej elementy Przypisania operator =(X) operator przypisania assign(n, x) przypisz n kopii x assign(first, last) przypisz elementy z [first, last) Dodawanie i usuwanie elementów z kolekcji push_back(x) wstaw na koniec x pop_back( ) usuń ostatni element push_front(x) wstaw na początek x (dla list) pop_front( ) usuń pierwszy element (dla list) insert(p,x) wstaw x przed p insert(p,n,x) wstaw n kopii x przed p insert(p, first, last)wstaw elementy z [first, last) przed p erase(p) usuń element wskazywany przez p erase(first, last) usuń [first, last) clear( ) usuń wszystkie elementy 16
Wszystkie kolekcje dostarczają operacje dotyczące między innymi liczby elementów. size( ) liczba elementów empty( ) czy kolekcja jest pusta max_size( ) największy dopuszczalny rozmiar capacity( ) pamięć przydzielona na wektor (tylko wektory) reserve(n) przydziel pamięć dla n elementów (tylko wektory) resize(n) zmień rozmiar kolekcji na n (tylko dla wektorów, list i kolejek) W przykładach będziemy korzystać z szablonu: int tab[7] = -1, 6, 3, -1, -8, 9, 2; vector<int> vec (tab + 2, tab + 5); vec.pop_back( ); vec.push_back(123); void wypisz(typ &co) if (co.empty( )) cout<<"brak elementow w kolekcji"<<endl; return; for (Typ::iterator it=co.begin( ); it!=co.end( );++it) cout<<*it<<" "; cout<<endl; 17 string str[5]="styczen", "luty", "maj", "czerwiec", "grudzien"; list<string> miesiace(str,str+5); miesiace.pop_front( ); miesiace.push_back("marzec"); miesiace.push_front("lipiec"); miesiace.pop_back( ); 3-1 -8 3-1 123 styczen luty maj czerwiec grudzien luty maj czerwiec grudzien marzec lipiec luty maj czerwiec grudzien 18 int tab[7] = -1, 6, 3, -1, -8, 9, 2; vector<int> vec (tab + 2, tab + 5); vec.insert(vec.begin( )+2,100); vec.insert(vec.begin( )+1,vec.begin( ), vec.end( )); vec.insert(vec.begin( )+6,-10); vec.erase(vec.begin( )+3); vec.erase(vec.begin( ), vec.begin( )+2); vec.clear( ); 3-1 -8 3-1 100-8 3 3-1 100-8 -1 100-8 3 3-1 100-8 -1-10 100-8 3 3-1 -8-1 -10 100-8 -1-8 -1-10 100-8 Brak elementow w kolekcji 19 Algorytmy uogólnione, realizują najczęściej wykonywane operacje i stosują się do rozmaitych typów zbiorczych nie tylko do kolekcji, takich jak wektor lub lista, lecz takŝe do wbudowanego typu tablicowego. Do algorytmu uogólnionego, który ma działać na jakiejś kolekcji, przekazuje się kolekcję za pośrednictwem pary iteratorów zaznaczających Ŝądany podzbiór elementów. Niemal wszystkie algorytmy uogólnione jako pierwsze dwa argumenty pobierają parę iteratorów, które określają zakres, na którym ma pracować algorytm. Zakres ten podaje się zwykle jako przedział domknięty z lewej strony. Para iteratorów musi spełniać następujący warunek: poczynając od wartości iteratora pierwszego i powtarzając wykonanie operatora zwiększania, moŝna zawsze osiągnąć wartość iteratora ostatniego. UŜywając któregokolwiek algorytmu uogólnionego, musimy dołączyć do programu plik nagłówkowy <algorithm> 20
Algorytmy nie modyfikujące kolekcji: Algorytm count( ) zlicza ile razy pojawiła się w przekazanym zakresie wartość trzeciego argumentu. Wykorzystuje operator ==. find( ) find_if( ) find_first_of( ) adjacent_find( ) count( ) count_if( ) equal( ) search( ) find_end( ) search_n( ) min_element( ) max_element( ) znajdź pierwsze wystąpienie wartości w ciągu znajdź w ciągu pierwsze wystąpienie wartości spełniającej predykat znajdź pierwsze wystąpienie wartości z jednego ciągu w innym znajdź pierwszą sąsiadującą parę wartości równych policz wystąpienia wartości w ciągu policz w ciągu wystąpienia wartości spełniających predykat prawda, jeśli elementy dwóch ciągów są parami równe znajdź pierwsze wystąpienie ciągu jako podciągu znajdź ostatnie wystąpienie ciągu jako podciągu znajdź n kolejnych wystąpień wartości w ciągu znajdź element najmniejszy kolekcji znajdź element największy kolekcji int tab[10] = -1, 6, 7, 3, -1, -8, 9, 2, 3; int ile=count(wektor.begin( ), wektor.end( ), 3); cout<<"wartosc 3 wystapila "<<ile<<" raz(y)"<<endl; Sprawdzam ile razy wystąpiła liczba parzysta: class Parzysta bool operator( )(int wrt) return (wrt%2==0 && wrt!=0)? true: false; ; Wartosc 3 wystapila 2 raz(y) 21 int tab[10] = -1, 6, 7, 3, -1, -8, 9, 2, 3; Wartosc parzysta wystapila 3 raz(y) int ile=count_if(wektor.begin( ), wektor.end( ), Parzysta( )); 22 cout<<"wartosc parzysta wystapila "<<ile<<" raz(y)"<<endl; template <class InputIterator, class T> int count (InputIterator first, InputIterator last, const T& val) int res=0; while (first!=last) if (*first++ ==val) ++res; return res; template <class InputIterator, class Pred> int count_if (InputIterator first, InputIterator last, Pred p) int res=0; while (first!=last) if (p(*first++)) ++res; return res; 23 class PodzielnaPrzez PodzielnaPrzez(int a=2) : liczba(a) bool operator( ) (int wrt) return (wrt%liczba==0 && wrt!=0)? true: false; private: int liczba; ; int tab[10] = -1, 6, 7, 3, -1, -8, 9, 2, 3; int ile=count_if(wektor.begin( ), wektor.end( ), PodzielnaPrzez(3)); cout<<"wartosc podzielna przez 3 wystapila "<<ile<<" raz(y)"<<endl; Zliczanie liczb dodatnich Wartosc podzielna przez 3 wystapila 4 raz(y) int tab[10] = -1, 6, 7, 3, -1, -8, 9, 2, 3; Wartosc dodatnia wystapila 6 raz(y) int ile=count_if(wektor.begin( ), wektor.end( ), bind2nd(greater<int>( ),0)); cout<<"wartosc dodatnia wystapila "<<ile<<" raz(y)"<<endl; 24
W bibliotece standardowej znajduje się zdefiniowany pierwotnie zbiór arytmetycznych, relacyjnych i logicznych obiektów funkcyjnych. Arytmetyczne obiekty funkcyjne: plus<typ>( ), minus<typ>( ), negate<typ>( ), multiplies<typ>( ), divides<typ>( ), modulus<typ>( ) Relacyjne obiekty funkcyjne less<typ>( ), less_equal<typ>( ), greater<typ>( ), greater<typ>( ), equal_to<typ>( ), not_equla_to<typ>( ) Logiczne obiekty funkcyjne logical_and<typ>( ), logical_or<typ>( ), logical_not<typ>( ) Aby uŝyć pierwotnie zdefiniowanych obiektów funkcyjnych, musimy dołączyć do programu plik nagłówkowy <functional> Adaptory obiektów funkcyjnych (wiązadła) przekształcają dwuargumentowy obiekt funkcyjny w obiekt jednoargumentowy związując jeden z argumentów z konkretną wartością: bind1st(nazwaklasy( ),x) - wywołaj funkcję dwuargumentową, której pierwszym argumentem jest x bind2nd(nazwaklasy( ),y) - wywołaj funkcję dwuargumentową, której pierwszym argumentem jest y 25 Algorytmy modyfikujące kolekcje transform( ) zastosuj operację do kaŝdego elementu w ciągu copy( ) kopiuj ciąg począwszy od pierwszego elementu copy_backward( ) kopiuj począwszy od ostatniego elementu. replace( ) zastąp elementy podaną wartością replace_if( ) zastąp elementy spełniające predykat replace_copy( ) kopiuj ciąg zastępując elementy podaną wartością replace_copy_if( ) kopiuj ciąg zastępując elementy spełniające predykat fill( ) zastąp kaŝdy element podaną wartością fill_n( ) zastąp pierwszych n elementów podaną wartością remove( ) usuń element o podanej wartości remove_if( ) usuń elementy spełniające predykat remove_copy( ) kopiuj ciąg, usuwając elementy o podanej wartości remove_copy_if( ) kopiuj ciąg, usuwając elementy spełniające predykat unique( ) usuń równe elementy sąsiadujące unique_copy( ) kopiuj, usuwając równe elementy sąsiadujące reverse( ) odwróć kolejność elementów reverse_copy( ) kopiuj ciąg odwracając kolejność elementów rotate( ) dokonaj rotacji elementów rotate_copy( ) kopiuj ciąg dokonując rotacji elementów sort( ) sortuj elementy kolekcji 26 Algorytm sort( ) zmienia kolejność elementów naleŝących do zakresu oznaczonego parą iteratorów [pierwszy, ostatni) i tworzy ciąg uporządkowany, korzystając z operatora mniejsze. int tab[10] = -1, 6, 7,3, -1, -8, 9, 2,3; sort(wektor.begin( ), wektor.end( )); sort(wektor.rbegin( ), wektor.rend( )); -8-1 -1 0 2 3 3 6 7 9 9 7 6 3 3 2 0-1 -1-8 class MniejszeNiz styczen luty maj czerwiec grudzien maj luty styczen czerwiec grudzień bool operator( ) (const string &a, const string &b) return a.size( )<b.size( ); string str[5]="styczen", "luty", "maj", "czerwiec", "grudzien"; vector<string> miesiace(str, str+5); sort(miesiace.begin( ), miesiace.end( ), MniejszeNiz( )); 27