Zrozumienie szablonów ma ogromne znaczenie dla zrozumienia terminologii, ponieważ sposób, w jaki o nich mówisz, determinuje sposób myślenia o nich.
W szczególności Area
nie jest klasą szablonu, ale szablonem klasy. Oznacza to, że jest to szablon, z którego można generować klasy. Area<int>
jest taką klasą (to nie jest obiekt, ale oczywiście możesz stworzyć obiekt z tej klasy w taki sam sposób, jak tworzysz obiekty z dowolnej innej klasy). Byłaby inna taka klasa Area<char>
. Zauważ, że są to zupełnie różne klasy, które nie mają ze sobą nic wspólnego poza tym, że zostały wygenerowane z tego samego szablonu klas.
Ponieważ Area
nie jest klasą, nie możesz wyprowadzić Rectangle
z niej klasy . Możesz wyprowadzić klasę tylko z innej klasy (lub kilku z nich). Ponieważ Area<int>
jest to klasa, możesz na przykład wyprowadzić Rectangle
z niej:
class Rectangle:
public Area<int>
{
// ...
};
Ponieważ Area<int>
i Area<char>
są różnymi klasami, możesz nawet wyprowadzać z obu jednocześnie (jednak podczas uzyskiwania dostępu do ich członków będziesz musiał poradzić sobie z niejasnościami):
class Rectangle:
public Area<int>,
public Area<char>
{
// ...
};
Jednak podczas definiowania musisz określić, z których klas ma pochodzić Rectangle
. Dzieje się tak bez względu na to, czy te klasy są generowane z szablonu, czy nie. Dwa obiekty tej samej klasy po prostu nie mogą mieć różnych hierarchii dziedziczenia.
Możesz także zrobić Rectangle
szablon. Jeśli piszesz
template<typename T> class Rectangle:
public Area<T>
{
// ...
};
Masz szablon, Rectangle
z którego możesz pobrać klasę, z Rectangle<int>
której pochodzi Area<int>
, i inną klasę, z Rectangle<char>
której pochodzi Area<char>
.
Może się zdarzyć, że chcesz mieć jeden typ Rectangle
, aby móc przekazywać różne rodzaje Rectangle
do tej samej funkcji (która sama nie musi znać typu obszaru). Ponieważ Rectangle<T>
klasy generowane przez tworzenie wystąpienia szablonu Rectangle
są formalnie niezależne od siebie, nie działa to w ten sposób. Możesz jednak skorzystać z wielokrotnego dziedziczenia tutaj:
class Rectangle // not inheriting from any Area type
{
// Area independent interface
};
template<typename T> class SpecificRectangle:
public Rectangle,
public Area<T>
{
// Area dependent stuff
};
void foo(Rectangle&); // A function which works with generic rectangles
int main()
{
SpecificRectangle<int> intrect;
foo(intrect);
SpecificRectangle<char> charrect;
foo(charrect);
}
Jeśli ważne jest, aby Twój rodzajowy Rectangle
wywodził się z generycznego Area
, możesz zrobić tę samą sztuczkę z Area
:
class Area
{
// generic Area interface
};
class Rectangle:
public virtual Area // virtual because of "diamond inheritance"
{
// generic rectangle interface
};
template<typename T> class SpecificArea:
public virtual Area
{
// specific implementation of Area for type T
};
template<typename T> class SpecificRectangle:
public Rectangle, // maybe this should be virtual as well, in case the hierarchy is extended later
public SpecificArea<T> // no virtual inheritance needed here
{
// specific implementation of Rectangle for type T
};