Dziedziczenie z klasy szablonu w języku C ++


106

Powiedzmy, że mamy klasę szablonu Area, która ma zmienną składową T area, a T getArea()i void setArea(T)funkcje składowe.

Potrafię stworzyć Areaobiekt określonego typu pisząc Area<int>.

Teraz mam klasę, Rectanglektóra dziedziczy Areaklasę. Ponieważ Rectanglesam w sobie nie jest szablonem, nie mogę pisać Rectangle<int>.

Jak wyspecjalizować dziedziczony Areatyp Rectangleobiektów?

EDYCJA: Przepraszam, zapomniałem wyjaśnić - moje pytanie dotyczy tego, czy można dziedziczyć Area bez specjalizacji, więc nie jest ona dziedziczona jako Area of ​​ints, ale ponieważ Area Rectangle może specjalizować typy.


4
Jest to szablon klasy , ponieważ jest to szablon, z którego są generowane klasy.
— sbi

1
@sbi Nie zamierzam tutaj rozpoczynać wojny o płomienie, ale jeśli Bjarne Stroustrup nie rozróżnia między szablonem klasy a klasą szablonu (zobacz Język programowania C ++ , wydanie 4, sekcja 23.2.1), to nie powinieneś też.
— Michael Warner,

@MichaelWarner Wydaje mi się, że pamiętam, jak dokonywał rozróżnienia. To było jednak w latach 90. w Usenecie. Może od tego czasu się poddał. (A może odnosi się do utworzonego szablonu klasy jako klasy szablonu?)
— sbi

Odpowiedzi:


245

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 Areanie 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ż Areanie jest klasą, nie możesz wyprowadzić Rectanglez 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ć Rectanglez 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ć Rectangleszablon. Jeśli piszesz

template<typename T> class Rectangle:
  public Area<T>
{
  // ...
};

Masz szablon, Rectanglez 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 Rectangledo tej samej funkcji (która sama nie musi znać typu obszaru). Ponieważ Rectangle<T>klasy generowane przez tworzenie wystąpienia szablonu Rectanglesą 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 Rectanglewywodził 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
};

Uwaga: Parametr typu szablonu jest częścią klasy obiektu, który tworzy wystąpienie tego szablonu klasy. Innymi słowy, nie jest to składnik klasy, jest częścią typu.
— Nikos

21

Czy po prostu próbujesz wyprowadzić Area<int>? W takim przypadku robisz to:

class Rectangle : public Area<int>
{
    // ...
};

EDYCJA: Po wyjaśnieniu wygląda na to, że faktycznie próbujesz również utworzyć Rectangleszablon, w takim przypadku następujące czynności powinny działać:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};


8

Uczyń Rectangle szablonem i przekaż nazwę typu do Area:

template <typename T>
class Rectangle : public Area<T>
{

};

6

Rectanglebędzie musiał być szablonem, w przeciwnym razie jest to tylko jeden typ . Nie może być nie szablonem, podczas gdy jego podstawa jest magicznie. (Jego podstawą może być wystąpienie szablonu , chociaż wydaje się, że chcesz zachować funkcjonalność bazy jako szablonu ).


3
#include<iostream>

using namespace std;

template<class t> 
class base {
protected:
    t a;
public:
    base(t aa){
        a = aa;
        cout<<"base "<<a<<endl;
    }
};

template <class t> 
class derived: public base<t>{
    public:
        derived(t a): base<t>(a) {
        }
        //Here is the method in derived class 
    void sampleMethod() {
        cout<<"In sample Method"<<endl;
    }
};

int main() {
    derived<int> q(1);
    // calling the methods
    q.sampleMethod();
}

co by było, gdybyś miał metody w klasie pochodnej? Jak byś je zdefiniował? A większość jeszcze importuje, przynajmniej dla mnie, jak wywołać / użyć tych metod w funkcji main ()?
— Pototo

6
Po pierwsze, jest to naprawdę stare pytanie, na które już jest świetna odpowiedź. Po drugie, unikaj odpowiedzi (i pytań), które są tylko kodem. Warto również dołączyć szczegółowe wyjaśnienia.
— marcman
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.