Hériter d'une classe de modèle en C ++

106

Disons que nous avons une classe de modèle Area, qui a une variable membre T area, a T getArea()et une void setArea(T)fonction membre.

Je peux créer un Areaobjet d'un type spécifique en tapant Area<int>.

Maintenant, j'ai une classe Rectanglequi hérite de la Areaclasse. Puisque Rectanglelui-même n'est pas un modèle, je ne peux pas taper Rectangle<int>.

Comment spécialiser le Areatype hérité des Rectangleobjets?

EDIT: Désolé, j'ai oublié de clarifier - ma question est de savoir s'il est possible d'hériter de Area sans le spécialiser, donc il n'est pas hérité en tant que Area of ​​ints mais en tant que Area Rectangle peut spécialiser les types.

dtech
la source
3
C'est un modèle de classe , car c'est un modèle à partir duquel les classes sont générées.
sbi
1
@sbi Je n'ai pas l'intention de lancer une guerre des flammes ici, mais si Bjarne Stroustrup ne fait pas de distinction entre le modèle de classe et la classe de modèle (voir Le langage de programmation C ++ , 4e éd., section 23.2.1), alors vous ne devriez pas pas non plus.
Michael Warner
@MichaelWarner J'ai l'impression de me souvenir qu'il a fait la distinction. C'était dans les années 90, cependant, sur Usenet. Peut-être qu'il a abandonné depuis. (Ou peut-être qu'il fait référence à un modèle de classe instancié en tant que classe de modèle?)
sbi

Réponses:

244

Pour comprendre les modèles, il est extrêmement avantageux de clarifier la terminologie, car la façon dont vous en parlez détermine la façon de les penser.

Plus précisément, ce Arean'est pas une classe de modèle, mais un modèle de classe. Autrement dit, il s'agit d'un modèle à partir duquel des classes peuvent être générées. Area<int>est une telle classe (ce n'est pas un objet, mais bien sûr, vous pouvez créer un objet à partir de cette classe de la même manière que vous pouvez créer des objets à partir de n'importe quelle autre classe). Une autre classe serait Area<char>. Notez que ce sont des classes complètement différentes, qui n'ont rien de commun si ce n'est qu'elles ont été générées à partir du même modèle de classe.

Puisque ce Arean'est pas une classe, vous ne pouvez pas en dériver la classe Rectangle. Vous ne pouvez dériver une classe qu'à partir d'une autre classe (ou de plusieurs d'entre elles). Puisque Area<int>est une classe, vous pouvez, par exemple, en dériver Rectangle:

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

Puisque Area<int>et Area<char>sont des classes différentes, vous pouvez même dériver des deux en même temps (cependant, lorsque vous accédez à leurs membres, vous devrez faire face à des ambiguïtés):

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

Cependant, vous devez spécifier de quelle classe dériver lorsque vous définissez Rectangle. Cela est vrai, que ces classes soient générées à partir d'un modèle ou non. Deux objets de la même classe ne peuvent tout simplement pas avoir des hiérarchies d'héritage différentes.

Vous pouvez également créer Rectangleun modèle. Si vous écrivez

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

Vous avez un modèle à Rectanglepartir duquel vous pouvez obtenir une classe Rectangle<int>qui dérive Area<int>et une classe différente Rectangle<char>qui dérive Area<char>.

Il se peut que vous souhaitiez avoir un seul type Rectanglepour pouvoir passer toutes sortes de choses Rectangleà la même fonction (qui elle-même n'a pas besoin de connaître le type Area). Étant donné que les Rectangle<T>classes générées par l'instanciation du modèle Rectanglesont formellement indépendantes les unes des autres, cela ne fonctionne pas de cette façon. Cependant, vous pouvez utiliser l'héritage multiple ici:

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);
}

S'il est important que votre générique Rectanglesoit dérivé d'un générique, Areavous pouvez également faire la même chose 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
};
celtschk
la source
Remarque: Un paramètre de type de modèle fait partie de la classe d'un objet qui instancie ce modèle de classe. En d'autres termes, ce n'est pas un composant de la classe, cela fait partie du type.
Nikos
21

Essayez-vous simplement de dériver Area<int>? Dans quel cas vous faites ceci:

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

EDIT: Après la clarification, il semble que vous essayez également de créer Rectangleun modèle, auquel cas ce qui suit devrait fonctionner:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};
Stuart Golodetz
la source
8
class Rectangle : public Area<int> {
};
ldanko
la source
8

Faites de Rectangle un modèle et passez le nom de type à Area:

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

};
pezcode
la source
6

Rectangledevra être un modèle, sinon ce n'est qu'un type . Il ne peut pas être un non-modèle tant que sa base l'est comme par magie. (Sa base peut être une instanciation de modèle , bien que vous sembliez vouloir conserver les fonctionnalités de la base en tant que modèle .)

Courses de légèreté en orbite
la source
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();
}
Narendra Kumawat
la source
et si vous aviez des méthodes dans la classe dérivée? Comment les définiriez-vous? Et la plupart importent encore, du moins pour moi, comment appelez-vous / utilisez-vous ces méthodes dans votre fonction main ()?
Pototo
6
Premièrement, c'est une question vraiment ancienne qui a déjà une excellente réponse. Deuxièmement, veuillez éviter les réponses (et questions) qui ne sont que du code. Il est également utile d'inclure des explications détaillées.
marcman