Zaawansowane programowanie w C++ (PCP) Wykład 6 - szablony.
dr inż. Robert Nowak
- p. 1/15
Poj ęcia niezależne od typu:
kolekcje (np. listy)
algorytmy (np. znajdowania najwi ększego elementu)
Mechanizmy eliminujące redundancj ę kodu.
„r ęczna” modyfikacja kodu
» Podział kodu wzorca na pliki
wspólna klasa bazowa
wykorzystanie szablonów
- p. 2/15
typedef int Element;//element przykładowego kontenera
class WektorInt {
public:
explicit WektorInt(int size = 10);
//konstruktor kopiujący, operator przypisania, destruktor
const Element& get(int idx) const;//Zwraca element o danym
Element& get(int idx);//Zwraca element o danym indeksie
» Podział kodu wzorca na pliki
//...
private:
Element* tab_;//Tablica przechowująca elementy wektora int size_;//Liczba elementów
int capacity_;//Wielkość tablicy
};
konieczno ść r ęcznej zmiany kodu (bł ędy!)
utrudnione wprowadzanie modyfikacji (wiele kopii kodu)
- p. 3/15
#include Öbject.h¨
class WektorObj {
public:
explicit WektorInt(int size = 10);
//konstruktor kopiujący, operator przypisania, destruktor
const Object& get(int idx) const;//Zwraca element o danym
Object& get(int idx);//Zwraca element o danym indeksie
//...
» Podział kodu wzorca na pliki
private:
Object** tab_;//Tablica przechowująca elementy wektora
//...
};
Wady:
narzuty pami ęciowe i czasowe
problem typów wbudowanych
- p. 4/15
tempate<class T> class Wektor {
public:
explicit Wektor(int size = 10);
//konstruktor kopiujący, operator przypisania, destruktor
const T& get(int idx) const;//Zwraca element o danym indeksie
T& get(int idx);//Zwraca element o danym indeksie
void add(const T& val);//Dodaje element na koniec kolekcji
/
» Podział kodu wzorca na pliki
* Dalsza część interfejsu klasy */
private:
T* tab_;//Tablica przechowująca elementy wektora int size_;//Liczba elementów
int capacity_;//Wielkość tablicy
};
Wektor<int> v;//Kolekcja liczb całkowitych v.add(3); v.add(4);//Operacje na kolekcji Wektor<Data> vd;//Kolekcja obiektów klasy Data
- p. 5/15
Wektor<Figura*> vf;//Kolekcja wskaźników do klasy
Deklaracja szablonu:
deklaracja-wzorca:
template < lista-parametrów-wzorca > deklaracja
lista-parametrów-wzorca:
parametr-wzorca [,lista-parametrów-wzorca]
parametr-wzorca:
class identyfikator [= id-typu ]
» Podział kodu wzorca na pliki
typename identyfikator [= id-typu ]
Przykłady:
template<class T> class Array { /* oparty o jeden typ */ }; template<typename T, typename U>
class Graf{ /* zależny od dwu typów */ }; template<typename T, typename U = int> class BinTree{ /* Domyślny parametr szablonu */ };
- p. 6/15
nazwa-szablonu < typ [,typ] >
Przykłady:
Array<int> a;
std::vector<double> vd;
Graf<std::string, std::string> gr;
vector<vector<int> > vv;
» Podział kodu wzorca na pliki
Niejednoznaczności:
zbyt bliskie poło żenie ko ńczących > , np.
vector<vector<int>> v;
znak >> jest operatorem przesuni ęcia
prawidłowe vector<vector<int> > v; (dodatkowa spacja)
- p. 7/15
Szablony są mechanizmem czasu kompilacji
pozwalają współdzielić kod źródłowy
brak narzutów czasowych w czasie wykonania
Organizacja plików źródłowych
definicja (a nie tylko deklaracja) szablonu musi by ć widoczna
w miejscu użycia
» Podział kodu wzorca na pliki
organizacja plików źródłowych
Kod nie jest kompilowany, jeżeli nie został użyty
- p. 8/15
Definicja znajduje si ę w <utility>
template<class _T1, class _T2>
struct pair {
typedef _T1 first_type;
typedef _T2 second_type;
_T1 first;//Pierwsza składowa
_T2 second;//Druga składowa
» Podział kodu wzorca na pliki
pair() : first(), second() { }
pair(const _T1& __a, const _T2& __b)
: first(__a), second(__b) { }
};
Przykłady użycia:
std::pair<std::string,bool> a(Äla¨,true); a.first = Öla¨;
- p. 9/15
a.second = false;
Wzorce funkcji: pozwalają implementować algorytmy
niezależne od typu
template<typename T>
void std::swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
» Podział kodu wzorca na pliki
}
template<typename T>
void printAll(const vector<T>& v, std::ostream& os) {
for(size_t i = 0; i != v.size(); ++i)
os << v[i] << ","; os << endl;
}
- p. 10/15
Istnieje możliwość wyznaczania argumentów wzorca z
argumentów podanych w wywołaniu
template<typename T>
void printAll(const vector<T>& v, std::ostream& os) { /
* ... *
vector<int> w;
//dodanie elementów do wektora
» Podział kodu wzorca na pliki
printAll(w,cout);//printAll<int>(w,cout) template<typename T> T* przydziel()
{ /* Tworzy obiekt i zwraca wskaźnik */ }
int* pi = przydziel<int>();//Niemożliwa automatyczna specjalizacja
- p. 11/15
Typowy problem - jawne wskazanie, że identyfikator jest
typem
class Foo {
public:
typedef int Element;
/* dalsza część implementacji klasy Foo */
» Podział kodu wzorca na pliki
};
template<typename T> void f(const T& t) {
//Poniżej błędnie zakłada, że T::Element to nazwa składowej T::Element e = 0;
//...
//Poniżej jawnie wskazano, że T::Element to nazwa typu typename T::Element e = 0;
//...
};
- p. 12/15
Szablony kontra hierarchia klas
Używaj szablonów gdy:
wa żne są typy wbudowane
nie mo żna utworzyć wspólnej klasy podstawowej
bardzo wa żna jest efektywność
akceptowalna jest powtórna kompilacja przy dodawaniu
nowego typu
» Podział kodu wzorca na pliki
W przeciwnym wypadku używaj hierarchii klas.
- p. 13/15
1. zaimplementować typ konkretny
2. przetestować go
3. przekształcić go w szablon
4. wygenerować i przetestować kilka różnych typów
» Podział kodu wzorca na pliki
- p. 14/15
Plik ¨kontener.h¨ - definicja klasy
#ifndef KONTENER
#define KONTENER
template<typename T>
class Kontener { /* Definicja klasy i metod inline */ };
#include ¨kontener_impl.h¨
» Podział kodu wzorca na pliki
#endif
Plik ¨kontener_impl.h¨ - implementacja metod wzorca
//Nie ma zabezpieczeń przed wielokrotnym dołączaniem template<typename T>
T& Kontener<T>::get(int index) { /* Definicja metody */ }
Plik ¨kontener.cpp¨ - kod niezależny od typu T
- p. 15/15