Czym są? Szablony (wzorce) są czymś podobnym do makr, tyle że wykonywane są przez kompilator, a nie przez preprocesor. Cechują je pewne własności, których jednak nie ma preprocesor, np. można tworzyć rekurencyjne wywołania. Ale zobaczmy najpierw taką sytuację: chcemy utworzyć klasę o nazwie Punkt o trzech współrzędnych: x, y, z. Potrzebujemy trzy różne implementacje. Jedna ma działać na liczbach typu unsigned int, druga na liczbach typu int, a trzecia na float. Pierwsze, o czym pomyślimy, to napisać coś takiego (jeśli dostrzeżesz błąd, to się nie przejmuj). Mamy przykładową klasę:

// ... class PunktUInt { public: PunktUInt( unsigned argX, unsigned argY, unsigned argZ ) : x(argX), y(argY), z(argX)

unsigned x, y, z;

// ...

I potem co? kopiujemy i zmieniamy unsigned na int:

// ... class PunktInt { public: PunktInt( int argX, int argY, int argZ ) : x(argX), y(argY), z(argZ)

int x, y, z;

// ... Następnie zamieniamy na float:

// ... class PunktFloat { public: PunktFloat( float argX, float argY, float argZ ) : x(argX), y(argY), z(argX)

float x, y, z;

// ... Uff! Wreszcie napisaliśmy - pomyślisz sobie. Jednak pisanie wymaga trochę wysiłku. No dobrze, teraz tworzymy funkcję main:

int main(void) { PunktInt A(0,-10,0); PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl; std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

I oto po naszych ciężkich staraniach otrzymujemy dość ciekawy i niespodziewany wynik:

A(0,-10,0) B(0,10,0)

Ojej! - zapewne krzykniesz. Musiał gdzieś się tu zjawić błąd. Trzeba znaleźć go. Aha, mamy go:

// ... PunktUInt( unsigned argX, unsigned argY, unsigned argZ ) : x(argX), y(argY), '''z(argX)''' // ...

Zamiast z(argX) powinno być z(argZ). I trzeba teraz wszystko poprawiać, w tym także resztę funkcji... Dobrze, że to tylko tyle. Ale na szczęście C++ daje nam prostszy sposób.

Wyróżniamy różne możliwości szablonów:

Wykorzystywanie szablonów

Napiszmy teraz jeszcze raz nasz program. Tym razem z wykorzystaniem szablonów.

// ... template class Punkt { public: Punkt( T argX, T argY, T argZ ) : x(argX), y(argY), z(argX)

T x, y, z;

// ... Za pomocą template tworzymy nasz szablon. Parametrem jest typ, jaki chcemy użyć, tworzymy go poprzez . Teraz, aby utworzyć nasze punkty możemy zapisać:

Punkt A(0,-10,0); Punkt A(0,10,5);

Czyli nasz main będzie wyglądał tak:

int main(void) { Punkt A(0,-10,0); Punkt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z PunktInt; typedef Punkt PunktUInt; typedef Punkt PunktFloat; I tyle. Main zamieni nam się wtedy w pierwotną formę:

int main(void) { PunktInt A(0,-10,0); PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl; std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

Powiesz:

I masz rację, bo zobaczysz:

A(0,-10,0) B(0,10,0)

Powód jest ten sam co poprzedni. Punkt( T argX, T argY, T argZ ) : x(argX), y(argY), '''z(argX)'''

Musimy zamienić z(argX) na z(argZ) i będzie wszystko w porządku. Tylko tyle. Nie wierzysz? Ale to jest prawda. To zobacz cały nasz program powinien wyglądać w ten sposób: #include

template class Punkt { public: Punkt( T argX, T argY, T argZ ) : x(argX), y(argY), z(argZ)

T x, y, z;

typedef Punkt PunktInt; typedef Punkt PunktUInt; typedef Punkt PunktFloat;

int main(void) { PunktInt A(0,-10,0); PunktUInt B(0,10,5);

std::cout << "A(" << A.x << "," << A.y << "," << A.z << ")" << std::endl; std::cout << "B(" << B.x << "," << B.y << "," << B.z << ")" << std::endl;

Szablony z wieloma parametrami

Szablon może także mieć więcej niż jeden parametr. Na przykład chcielibyśmy posługiwać się parami obiektów. Należy więc napisać klasę Para, zawierającą dwa elementy: pierwszy o nazwie pierwszy, a drugi o nazwie drugi, jednakże nie wiemy z góry, jakie mają one mieć typy. Możemy to zrobić w ten sposób:

#include #include template class Para { public: Para()

Para( T1 a, T2 b ) : pierwszy(a), drugi(b)

T1 pierwszy; T2 drugi;

int main(void) { // tworzymy nasz obiekt Para zmienna("Liczba",10);

std::cout utworzyliśmy szablon o dwóch parametrach.

Deklaracja zmiennej zmienna określa jej typ poprzez skonkretyzowanie typów w szablonie, pośrednio więc określa też, jakie typy będą miały składowe tej zmiennej.

Można też liczby

Parametrem szablonu może być także liczba. Zilustrujmy to przykładem: #include #include template class Tablica { public:

T &operator[]( std::size_t i )

return tabl[i];

private: T tabl[N];

int main(void) { Tablica A;

for ( int i=0; i<10; ++i )

A[i]=100+i;

for ( int i=0; i<10; ++i )

std::cout << "A[" << i << "]=" << A[i] << std::endl;

return 0; } W powyższym przykładzie użyto typu std::size_t, zadeklarowanego w pliku nagłówkowym dołączanym dyrektywą .