Programowanie obiektowe
Wykład 5. C++: szablony
Szablony
◮
Szablony to technika realizacji polimorfizmu na innym
poziomie niż za pomocą funkcji wirtualnych i dziedziczenia.
◮
Mechanizm ten można rozumieć jako ’inteligentniejsze’
makrodefinicje.
◮
Szablony nie są w bezpośredni sposób związane z
programowaniem obiektowym.
Szablony funkcji
przykład problemu: definiujemy funkcję porównującą...
...liczby całkowite:
int compare(int a, int b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
Szablony funkcji
przykład problemu: definiujemy funkcję porównującą...
...liczby całkowite:
int compare(int a, int b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
...liczby rzeczywiste:
int compare(float a, float b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
Szablony funkcji
przykład problemu: definiujemy funkcję porównującą...
...liczby całkowite:
int compare(
int
a,
int
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
...liczby rzeczywiste:
int compare(
float
a,
float
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
... i tak samo dla każdego innego typu
Szablony funkcji
przykład problemu: definiujemy funkcję porównującą...
...liczby całkowite:
int compare(
int
a,
int
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
...liczby rzeczywiste:
int compare(
float
a,
float
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
... i tak samo dla każdego innego typu
... pod warunkiem, że ma zdefiniowany operator <
Szablony funkcji
przykład problemu: definiujemy funkcję porównującą...
...liczby całkowite:
int compare(
int
a,
int
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
...liczby rzeczywiste:
int compare(
float
a,
float
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
...pudełka
class Pudelko
{
public:
float dl,sz,wy;
Pudelko(float d, float s, float w) : dl(d), sz(s), wy(w) {}
int operator<(Pudelko& p) { return dl*sz*wy < p.dl*p.sz*p.wy; }
};
int compare(
Pudelko
a,
Pudelko
b)
{ if(a<b) return 1; else if(b<a) return -1; else return 0; }
Szablony funkcji
podstawowe informacje
◮
szablon funkcji jest sparametryzowaną definicją funkcji
◮
szablon funkcji definiuje się w zakresie globalnym
◮
parametrem szablonu funkcji jest typ (klasa), bądź lista typów
◮
wszystkie parametry szablonu muszą wystąpić w opisie
argumentów funkcji szablonowej (kompilator odnajduje
szablon na podstawie wywołania funkcji)
Składnia:
template< opis˙parametrów >
definicja funkcji
Szablony funkcji
przykład
class Pudelko
{
public:
float dl,sz,wy;
Pudelko(float d, float s, float w) : dl(d), sz(s), wy(w) {}
int operator<(Pudelko& p) { return dl*sz*wy < p.dl*p.sz*p.wy; }
};
template <class T>
int compare(T a, T b)
{
if(a<b) return -1; else if(b<a) return 1; else return 0;
}
int main()
{
cout
<< compare(1,2) << endl
//-1
<< compare(1.5,2.5) << endl
//-1
<< compare(’a’,’b’) << endl
//-1
<< compare(Pudelko(1.0,2.0,2.0),Pudelko(2.0,1.5,4.2)) << endl;//-1
}
Szablony funkcji
parametry
◮
parametrem szablonu funkcji jest typ (klasa), bądź lista typów
◮
wszystkie parametry szablonu muszą wystąpić w opisie
argumentów funkcji szablonowej (kompilator odnajduje
szablon na podstawie wywołania funkcji)
◮
oprócz obowiązkowego użycia parametrów szablonu w opisie
argumentów funkcji szablonowej, można ich używać w
dowolnym miejscu w definicji tej funkcji (jako nazwy typu).
template<class S, class T>
T
* jakasfunkcja(S a, T b)
{
T
*l = new(
T
);
int r=sizeof(
S
);
...
return l;
}
Szablony funkcji
◮
jedynym ograniczeniem, jakie w definicji szablonu można
nałożyć na typy argumetów funkcji szablonowej jest by
niektóre z nich były takie same, np. szablon
template <class K, class L, class M>
void f(K a, L b, K c, M d)
...
pasuje do wywołania
f(3,’e’,5,"Ala")
ale do wywołania
f(3,’e’,’a’,"Ala")
nie.
Szablony funkcji
jak to działa
◮
natrafiając na wywołanie funkcji, kompilator sprawdza, czy
istnieje funkcja o podanej nazwie, liczbie i typie argumentów
◮
jeśli nie, sprawdza, czy istnieje szablon pozwalający taką
funkcję wygenerować, jeśli tak – generuje odpowiednią funkcję
◮
jeśli nie, sprawdza czy isnieje funkcja którą można dopasować
dokonując konwersji typów argumentów, jeśli tak, dokonuje
wiązania z zastosowaniem konwersji typów
Szablony funkcji
sytuacje wyjątkowe
◮
często zdarza się, że funkcje wygenerowane z szablonu będą
działać poprawnie (lub w ogóle będą poprawne) w większości
przypadków, ale nie we wszystkich
◮
szablon z poprzedniego przykładu nie będzie działał poprawnie
dla typu char*
◮
nie nadaje się on w ogóle np. dla typu
class Data {
public:
int dzien, miesiac, rok;
}
za względu na brak operatora <
cout
<< compare(1,2) << endl
// -1
<< compare(1.5,2.5) << endl
// -1
<< compare(’a’,’b’) << endl
// -1
<< compare(Pudelko(1.0,2.0,2.0),Pudelko(2.0,1.5,4.2)) << endl // -1
<< compare("Ala","Ola") << endl
// 1
(!)
<< compare((string)"Ala",(string)"Ola") << endl;
// -1
Szablony funkcji
funkcje specjalizowane
◮
wyjątki od sposobu generownia funkcji zdefiniowanego przez
szablon można zdefiniować pisząc funkcję specjalizowaną
◮
funkcja specjalizowana to funkcja, której nazwa i typy
parametrów pasują do szablonu, tyle że jest jest to normalna
funkcja
Szablony funkcji
funkcje specjalizowane
template <class T>
int compare(T a, T b)
{
if(a<b) return -1; else if(b<a) return 1; else return 0;
}
int compare(const char* a, const char* b)
{
return strcmp(a,b);
}
int main()
{
cout << compare(1,2) << endl
// -1
<< compare(1.5,2.5) << endl
// -1
<< compare(’a’,’b’) << endl
// -1
<< compare(Pudelko(1.0,2.0,2.0),Pudelko(2.0,1.5,4.2)) << endl // -1
<< compare("Ala","Ola") << endl
// -1
<< compare((string)"Ala",(string)"Ola") << endl;
// -1
}
Szablony klas
podstawowe informacje
◮
szablon klasy jest sparametryzowaną definicją klasy
◮
szablon klasy definiuje się w zakresie globalnym
Składnia:
template< opis˙parametrów >
class nazwaklasy
{
...
};
Szablony klas
przykład (bardzo typowy)
Szablon klasy Stos, którego parametrem jest typ elementów
przechowywanych na stosie.
template <class
T
>
class Stos
{
public:
Stos() : n(0)
}
Stos& push(
T
e) { dane[n++]=e; return *this; } // dodanie elementu
T
pop()
{ return dane[--n]; }
// pobranie elementu
int empty()
{ return n==0; }
// test czy pusty
operator int()
{ return n; }
// dodatkowy bajer
private:
T
dane[100];
int n;
};
Szablony klas
klasa szablonowa
◮
po zdefniowaniu szablonu klas
template< opis˙parametrów >
class nazwaklasy
{
...
};
nazwą klasy szablonowej jest nazwaklasy<parametry>
◮
posługujemy się nią jak zwykłymi nazwami klas/typów
◮
w momencie napotkania takiej nazwy, kompilator generuję
definicję klasy szablonowej dla podanych parametrów (jeśli
wcześniej już tego nie zrobił)
Szablony klas
użycie klasy szblonowej
int main()
{
Stos<char>
stosznakow;
// generowana jest klasa Stos<char>
Stos<int>
stosliczb;
// generowana jest klasa Stos<int>
stosznakow.push(’A’).push(’l’).push(’a’);
stosliczb.push(1).push(2).push(3);
Stos<char>
drugistosznakow=stosznakow; // klasa Stos<char> już jest
while(stosznakow)
// bajer
{
cout << stosznakow.pop();
}
cout << endl;
while(stosliczb)
{
cout << stosliczb.pop();
}
cout << endl;
}
Szablony klas
parametry
◮
Szablon klasy może mieć wiele parametrów (separatorem jest
przecinek)
◮
Parametrem szablonu klasy może być:
◮
typ (class nazwa)
◮
wartość całkowita stała (int nazwa)
template < class Typ, int rozmiar >
◮
także stałe wyrażenie będące adresem obiektu globalnego lub
funkcji globalnej (rzadsze zastosowanie)
Szablony klas
definicje metod klasy szablonowej poza ciałem klasy
◮
definicje metod klasy szablonowej umieszczone poza ciałem
klasy są w istocie szablonami definicji metod
◮
mają postać taką jak szablony funkcji, z tą oczywiście różnicą,
że ich nazwę poprzedza się nazwą klasy szablonowej
◮
podobnie też jak w przypadku szablonów funkcji, można
definiować metody specjalizowane
Szablony klas
definicje metod klasy szablonowej poza ciałem klasy
template <class T>
class Stos
{
public:
Stos();
Stos& push(T e);
T pop();
itd
private:
T dane[100];
int n;
};
template<class T>
Stos<T>::Stos() : n(0) {}
template<class T>
Stos<T>& Stos<T>::push(T e) { dane[n++]=e; return *this; }
template<class T>
T Stos<T>::pop()
{ return dane[--n]; }
itd.
Szablony klas
definicje metod klasy szablonowej poza ciałem klasy
(lupa)
◮
są to szablony metod
template<class T>
Stos<T>::Stos() : n(0) {}
template<class T>
Stos<T>& Stos<T>::push(T e) { dane[n++]=e; return *this; }
template<class T>
T Stos<T>::pop()
return dane[--n];
itd.
Szablony klas
definicje metod klasy szablonowej poza ciałem klasy
(lupa)
◮
nieodłączną częścią nazwy klasy szablonowej jest parametr(y)
template<class T>
Stos<T>
::Stos() : n(0) {}
template<class T>
Stos<T>
&
Stos<T>
::push(T e) { dane[n++]=e; return *this; }
template<class T>
T
Stos<T>
::pop()
return dane[--n];
itd.
Szablony klas
definicje metod klasy szablonowej poza ciałem klasy
(lupa)
◮
nazwa konstruktora (destruktora) nie jest nazwą klasy — bez
parametu
template<class T>
Stos<T>::
Stos
() : n(0) {}
template<class T>
Stos<T>& Stos<T>::push(T e) { dane[n++]=e; return *this; }
template<class T>
T Stos<T>::pop()
return dane[--n];
itd.
Szablony klas
parametry nie będące typami – przykład
W szablonie stosów dodajemy parametr określający rozmiar.
template <class T,
int max
>
class Stos
{
public:
Stos() : n(0)
{}
Stos& push(T e) { dane[n++]=e; return *this; }
T pop()
{ return dane[--n]; }
int empty()
{ return n==0; }
operator int()
{ return n; }
private:
T dane[
max
];
int n;
};
int main()
{
Stos<char,
20
> stos;
stos.push(’A’).push(’l’).push(’a’);
while(stos) { cout << stos.pop(); }
cout << endl;
}
Szablony klas
klasa szablonowa jako klasa bazowa
◮
Klasa szablonowa może być klasą bazową innej klasy.
◮
Klasa szablonowa może być klasą bazową w szablonie klasy.
template<class T>
class K
{
...
};
class L : K<int>
{
...
}
template<class S>
class M : K<int>
{
...
}
Szablony klas
klasa szablonowa jako klasa bazowa
◮
Klasa szablonowa może być klasą bazową innej klasy.
◮
Klasa szablonowa może być klasą bazową w szablonie klasy.
template<class T>
class K
{
...
};
class L : K<int>
{
...
}
template<class S>
class M : K<int>
{
...
}
Szablony klas
szablon klasy jako klasa bazowa
◮
szablonu klasy można użyć jako klasy bazowej w innym
szablonie klasy
◮
parametry szablonu klasy bazowej
muszą być parametrami
szablonu klasy pochodnej
albo muszą mieć ustaloną wartość
template<class T, int max>
class Stos
{
...
};
template<class
S
, int
max
>
class lepszystos : K<
S
,
max
>
{
...
}
template<class
S
>
class stosstulementowy : K<
S
,100>
{
...
}
Szablony klas
szablon klasy jako klasa bazowa
◮
szablonu klasy można użyć jako klasy bazowej w innym
szablonie klasy
◮
parametry szablonu klasy bazowej muszą być parametrami
szablonu klasy pochodnej
albo muszą mieć ustaloną wartość
template<class T, int max>
class Stos
{
...
};
template<class S, int max>
class lepszystos : K<S,max>
{
...
}
template<class S>
class stosstulementowy : K<S,
100
>
{
...
}
Szablony klas
składowe statyczne klas szablonowych
Każda instancja klasy szablonowej ma swój komplet składowych
statycznych.