Funkcje wzorcowe

Funkcje wzorcowe stosujemy jeśli potrzebujemy funkcji, które działają w ten sam sposób, a różnią się tylko typami używanych zmiennych. Roważmy na przykład funkcję

int max(int a, int b) {
   if(a>b) return a;
   else return b;
}
która zwraca większą z dwóch liczb. Jeśli potrzeba nam dodatkowo funkcji max, która porównuje liczby typu np. double musimy napisać drugą funkcję:
double max(double a, double b) {
   if(a>b) return a;
   else return b;
}
Nie różni się ona od poprzedniej niczym poza typami argumentów (i zwracanej wartości). Jak zapobiec pisaniu powtarzającego się kodu? Można zdefiniować funkcję wzorcową max.
template <class T>
T max(T a, T b) {
   if(a>b) return a;
   else return b;
}
Teraz max(a,b) będzie działać dla dowolnych typów, dla któych zdefiniowany jest operator porównania >.

Uwagi:

  1. Funkcja może być sparametryzowana przez więcej niż jedną klasę; wówczas klasy parametryzujące wymieniamy po przecinku, np. template <class S, class T>.
  2. Jeśli na podstawie typów parametrów nie można określić, który wzorzec należy wywołać, należy to jawnie podać, np. max<int>(x,y) wywołuje wzorzec dla typu int.

Klasy wzorcowe

Powyższy mechanizm można stosować również do klas i jest jeszcze bardziej użyteczny w tym przypadku. Oto przykład: rozważmy klasę Wektor, której obiektami są wektory na płaszczyznie, np. następującą:
class Wektor {
public:
   Wektor(double _x, double _y): x(_x), y(_y) {}
   Wektor operator+(Wektor w) { return Wektor(x+w.x, y+w.y); }
   double operator*(Wektor w) { return x*w.x+y*w.y); }
   void wypisz();
private:
   double x, y;
};
void Wektor::wypisz() {
   cout << "(" << x << "," << y << ")";
}
Co zrobić jeśli będziemy potrzebować wektorów o współrzędnych całkowitych (albo zespolonych)? Można napisać nową klasę (która będzie wyglądać dokładnie tak samo jak poprzednia), ale prościej jest stworzyć wzorzec klasy, tak jak poniżej:
template <class T>
class Wektor {
public:
   Wektor(T _x, T _y): x(_x), y(_y) {}
   Wektor operator+(Wektor w) { return Wektor(x+w.x, y+w.y); }
   T operator*(Wektor w) { return x*w.x+y*w.y); }
   void wypisz();
private:
   T x, y;
};
template <class T>
void Wektor<T>::wypisz() {
   cout << "(" << x << "," << y << ")";
}
Wzorce klas definiuje się tak samo jak klasy, z tym, że definicję należy poprzedzić słowem template z parametrem (listą parametrów); to samo dotyczy jej metod definiowanych na zewnątrz klasy. Teraz już można używać klas Wektor<double>, Wektor<int>, Wektor<zespolona> do reprezentowania odpowiednio wektorów rzeczywistych, całkowitych i zespolonych (zakładając, że klasa zespolona została zdefiniowana wcześniej).

Kolekcje standardowe

Kolekcje służą do przechowywania elementów pewnego określonego typu. W języku C++ udostępnione są biblioteki, które zawierają różne kolekcje, m. in. tablice, listy i kolejki. Ponieważ nie można z góry określić, jakiego typu elementów będziemy używać, wszystkie standardowe kolekcje są zdefiniowane jako wzorce. Oto zestawienie najważniejszych kolekcji standardowych:
  1. vector - tablica
  2. list - lista dwustronna
  3. deque - kolejka dwustronna - umożliwia szybki dostęp do wszystkich elementów oraz szybkie dodawanie i usuwanie elementów na końcach; wstawianie bądź usuwanie elementów w środku jest drogie
  4. queue - kolejka - umożliwia wstawianie na końcu i usuwanie z początku
  5. stack - stos - umożliwia wstawianie i usuwanie na końcu
  6. set - zbiór elementów
  7. map - słownik - przechowuje zbiór unikalnych kluczy oraz powiązanych z nimi wartości. Jest to wzorzec parametryzowany dwoma klasami (typem klucza i wartości); np. map<char, int> jest słownikiem o kluczach typu char i wartościach typu int.
  8. multiset - zbiór elementów z możliwymi powtórzeniami
  9. multimap - słownik, w którym klucze mogą się powtarzać
Jeśli chcemy użyć danej kolekcji, trzeba dołączyć odpowiednią bibliotekę - zazwyczaj o takiej samej nazwie jak dana kolekcja.