Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
Funkcjonalność obu specjalizacji jest taka sama.19 Stanowi to podkreślenie faktu, że klasy cech charakterystycznych mają zawierać właśnie cechy charakterystyczne, a to,
19Fakt, że char_traits<>::compare() może wywołać strcmp() w jednym wypadku, a w drugim wcscmp(), jest z naszego punktu widzenia nieistotny; wynik działania compare() jest taki sam.
Rozdział 5. ♦ Wszystko o szablonach
239
co zmienia się między tymi klasami, to zwykle typy i wartości stałe, ewentualnie al-
gorytmy stałe korzystające z parametrów szablonów związanych z typami. Klasy cech
charakterystycznych zwykle same są szablonami, gdyż zawarte w nich typy i stałe
mogą być rozpatrywane jako cechy charakterystyczne parametrów głównego szablonu
(na przykład char i wchar_t).
Dobrze byłoby móc też powiązać pewną funkcjonalność z argumentami szablonu, aby
program kliencki mógł łatwo dostosowywać je do swoich potrzeb. Na przykład po-
niższa wersja programu BearCorner obsługuje różne rodzaje rozrywek:
1.2@4144F!
L* D# #
: 4
: A@4144!A
7
d # # %#7*N ;* #* =
/
4 = 44 A*A "
"
;;
4 = 44 A# OA "
"
V 4# # 44# ##
V & =&
4 B V 4 V
@4144
V V
#; # 4 47# 47#
#; # 4 # #
47#
#
@4144 V 7 V 7 "
4
V A A ==
A A
A A
"
"
@# 4
@4144@#& / -4
-!4
@4
@4144@4& ;; F
F!4
" E
Parametr szablonu Action w klasie BearCorner powinien mieć statyczną funkcję składo-
wą doAction() używaną w BearCorner<>::entertain(). Użytkownicy mogą wybrać
Feed lub Stuff; obie te klasy zawierają odpowiednią funkcję. Klasy zawierające tak
240
Część II ♦ Standardowa biblioteka C++
zakodowane funkcje nazywamy klasami reguł. „Reguły” (ang. policy) przyjmowania go-
ści w powyższym programie są realizowane przez Feed::doAction() i Stuff::doAction().
Tutaj są to akurat zwykle klasy, ale równie dobrze mogłyby być szablonami; możliwe
byłoby też łączenie ich z dziedziczeniem. Więcej o projektowaniu przy użyciu klas
reguł znajdzie Czytelnik w książce Andrei Alexandrescu20; pozycja ta jest podstawowym
źródłem wiedzy na ten temat.
Tajemniczo powtarzający się wzorzec szablonów
Każdy początkujący programista C++ jest w stanie zmodyfikować klasę tak, aby śle-
dziła liczbę istniejących aktualnie obiektów tej klasy. Wystarczy dodać pola statyczne
i zmodyfikować konstruktor i destruktor:
1.211 !
M 8% # 4 #
: 4
7
11
11 GG "
11 11 GG "
E11 55 "
71 44 "
"
11 B .
11
11 71 -
11
11 71 F
%# O7
11
11 71 J
B
11 71 J
"
11 71 F
" E
Wszystkie konstruktory klasy CountedClass zwiększają wartość pola statycznego count,
zaś destruktory zmniejszają wartość tego pola. Statyczna funkcja składowa getCount()
zwraca aktualną liczbę obiektów.
Ręczne dodawanie wszystkich tych pól w każdej klasie, której obiekty chcesz zliczać,
jest zadaniem nudnym. Zwykle w językach obiektowych, jeśli kod się powtarza, ko-
rzysta się z dziedziczenia, ale w tym wypadku jest to półśrodek. Zauważ, co się stanie,
kiedy wydzielisz logikę zliczania do klasy bazowej:
20Modern C++ Design: Generic Programming and Design Patterns Applied, Addison Wesley, 2001.
Rozdział 5. ♦ Wszystko o szablonach
241
1.211 F!
@$O 48 8%
: 4
7
1
1 GG "
1 1 GG "
E1 55 "
71 44 "
"
1 B .
11 1 "
11 F 1 "
11
11 71 -
11
11 71 F
11 F
11 F71 J $N
" E
Wszystkie klasy pochodne Counted mają to samo statyczne pole, więc liczba obiek-
tów jest faktycznie liczbą wszystkich obiektów klas pochodnych tej klasy. Potrzebna
jest nam metoda automatycznego generowania różnych klas bazowych dla każdej klasy
pochodnej. Służy do tego dziwna konstrukcja szablonu pokazana poniżej:
1.211 J!
: 4
7
1
1 GG "
1 1 GG "
E1 55 "
71 44 "
"
1 B .
>% ;*
11 111 "
11 F 111 F "
11
11 71 -
11
242
Część II ♦ Standardowa biblioteka C++
11 71 F
11 F
11 F71 - I
" E
Każda klasa pochodna dziedziczy po innej klasie bazowej określanej przez użycie
samej siebie (klasy pochodnej) jako parametru szablonu! Wydawać się może, że jest
to definicja cykliczna; faktycznie, byłaby taką, gdyby jakiekolwiek pole klasy bazo-
wej użyło w obliczeniach argumentu szablonu. Jednak żadne pola Counted nie zależą
od T, wielkość Counted (wynosząca zero!) jest znana już podczas analizowania sza-
blonu. Wobec tego nie ma znaczenia, który argument zostanie użyty do ukonkretnie-