2009-08-31 4 views
3

maintenant j'apprends C++, et maintenant je connais le concept de base de modèle, qui agissent comme un type générique, et j'ai trouvé presque tous les programmes C++ utilisés modèle, Donc, je veux vraiment savoir quand nous sommes supposés utiliser le modèle? Quelqu'un peut-il conclure votre expérience sur le modèle C++? Quand envisagez-vous d'utiliser le modèle?quand est-il préférable d'utiliser le modèle C++?

Supplément: si nous avons défini cette fonction

template <class myType> 
myType GetMax (myType a, myType b) { 
return (a>b?a:b); 
} 

mais nous voulons passer pour la comparaison d'un objet (classe d'auto-défini), comment pouvons-nous mettre en œuvre?

Supplement2: dans la réponse ci-dessous, quelqu'un a écrit ce code exemple

template <class myType> 
const myType& GetMax (const myType& a, const myType& b) { 
    return (a<b?b:a); 
} 

template <class myType, class Compare> 
const myType& GetMax (const myType& a, const myType& b, Compare compare) { 
    return (compare(a,b)?b:a); 
} 

est-ce correct? peut-on simplement passer un nom de fonction en paramètre de la classe myType?

+0

Les modèles ne sont pas toujours des types génériques. Les modèles peuvent également être des fonctions génériques, comme dans l'exemple que vous avez donné. – pyon

+0

Re: complément 2. Vous pouvez passer un pointeur vers une fonction ("nom de fonction") ou un foncteur (classe surchargeant l'opérateur()) en tant que troisième paramètre de la deuxième surcharge. – UncleBens

+0

@UncleBens: Bien sûr, c'est là que commence le vrai plaisir. Imaginez toutes les utilisations (et les abus!) Pour cela (surtout le dernier)! – pyon

Répondre

1

Re: complément. Si vous voulez passer une fonction de comparaison, vous pouvez fournir une autre surcharge:

template <class myType> 
const myType& GetMax (const myType& a, const myType& b) { 
    return (a<b?b:a); 
} 

template <class myType, class Compare> 
const myType& GetMax (const myType& a, const myType& b, Compare compare) { 
    return (compare(a,b)?b:a); 
} 

utilisation SAMLE: pour comparer des chaînes de style C:

bool c_strings_less(const char* a, const char* b) 
{ 
    return std::strcmp(a, b) < 0; //is a less than b 
} 

const char* greater = GetMax("hello", "world", c_strings_less); 

Voici comment fonctionne l'algorithme std :: max. (J'ai aussi fait quelques modifications, par exemple, il est habituel en C++ que les prédicats définissent une comparaison "inférieure à".)

Ou si vous avez demandé comment GetMax fonctionnerait pour des types arbitraires définis par l'utilisateur, ceux-ci doivent surcharger l'opérateur. > ou votre fonction entraînerait une erreur de compilation.

+0

comparer (a, b)? B: un est ce droit? Peut ici simplement passer une fonction en tant que paramètre? – MemoryLeak

+0

Modifié pour fournir un exemple d'utilisation. – UncleBens

0

si vous ne connaissez pas le type de votre variable ou que vous voulez faire de même pour de nombreux types de variables, vous pouvez utiliser le modèle ...

si vous voulez ajouter 2 int, vous voulez obtenir un int pour le retour si vous voulez ajouter 2 lits doubles, vous voulez obtenir un double pour le retour

vous utilisez donc ce modèle pour ..

3

Fondamentalement quand vous voulez créer une classe générique qui peut gérer la solution pour plusieurs types de classes, sans avoir à avoir une classe parent pour toutes les classes que vous voulez supporter.

Vous pouvez juste donner à la classe avec laquelle vous voulez travailler avec (meilleur exemple serait un conteneur, qui peut stocker tout type que vous passez avec la création)

//----- the container 
template <class T> 
class Stack 
{ 
public: 
    T* stackPtr; 
} 

//----- example 
void main() 
{ 
    typedef Stack<float> FloatStack; 
    typedef Stack<int> IntStack; 
} 

Maintenant vous pouvez stocker floats et ints avec la même classe sans avoir à écrire une classe spécifique pour chaque type.

+0

Les modèles ont également tendance à être beaucoup plus sûrs que les hiérarchies de classes. Dans l'exemple donné, un FloatStack traite uniquement les flottants et vous ne pouvez pas l'utiliser pour subvertir le système de types. Une implémentation comparable avec une classe de base de Stack et des sous-classes de FloatStack et IntStack serait beaucoup plus difficile à sécuriser, en particulier si la classe de base avait des détails d'implémentation afin qu'ils n'aient pas à être répétés dans chaque sous-classe. –

2

Réponse courte: si cela ne sert à rien: pas. S'il semble résoudre un problème (réutilisation de code sur différents types, ...), implémentez et déboguez d'abord sans les templates, puis ajoutez les paramètres du template. Avant STL/boost, ils étaient agréables à fabriquer des conteneurs.

+0

Avant STL/boost, ils étaient agréables à faire des conteneurs. Qu'est-ce que c'est? – MemoryLeak

+1

Maintenant, il y a déjà des conteneurs fabriqués, bien sûr.Et il vaut mieux instancier un modèle bien débogué que d'écrire le vôtre. – MSalters

+0

Je pense que c'est un bon conseil pour un débutant, même si un développeur C++ plus expérimenté pourrait bien aller chercher les templates immédiatement. –

1

Lorsque vous devez paramétrer un concept représenté par une classe.

Par exemple, si vous avez une classe qui représente un moyen de gérer un type d'objet

class MyThingManager 
{ 
    void add(MyThing& mything); 
    //... 
}; 

... vous avez besoin plus tard peut-être utiliser exactement le même comportement dans un nouveau type, mais la gestion d'un autre type. Ensuite, vous avez le choix d'utiliser le copier/coller/remplacer --Que conduirait l'ouverture de l'enfer sous vos pieds immediately-- ou faire de votre classe ont un type de gestion comme parametter:

template< class ThingType > 
class ThingManager 
    { 
     void add(ThingType& thing); 
     //... 
    }; 

De cette façon, vous ne dupliquer le code.

Une autre préoccupation est quand vous voulez un appel de fonction pour être compatible avec tous les paramètres qui ont le besoin sémantique:

template< class MyType > 
void addPi(MyType& value) 
{ 
    value += PI; 
} 

De cette façon, vous (encore) ne doivent pas dupliquer du code pour chaque type possibles parametres.

Ce n'est pas appelé "programmation générique" pour rien.

Ce sont des cas simples, mais des cas plus complexes existent, lorsque vous voulez faire de la méta-programmation. Si vous voulez y aller, s'il vous plaît lire au moins un livre avant d'écrire le code de l'enfer. Je recommande "Meta-Programming Template C++" pour cela et l'excellent livre "Modern C++ Design" pour l'utilisation de modèles plus avancés comme le modèle de politique et d'autres bien connus.

8

G'day,

réponse simple est quand vous voulez que le comportement reste le même indépendamment du type utilisé pour instancier la classe. Par conséquent, une pile d'ints se comportera de la même manière qu'une pile de flottants se comportera comme une pile d'objets MyClass.

Les classes d'héritage et de base sont utilisées lorsque vous souhaitez autoriser la spécialisation du comportement. Par exemple, vous avez une classe de base appelée Animal et une fonction membre appelée makeSound(). Vous n'avez aucune idée du son que fera chaque animal, donc vous faites de la fonction membre makeSound une fonction virtuelle. En fait, comme il n'y a pas de son par défaut pour tous les animaux, vous n'avez aucune idée de ce qu'il doit avoir comme comportement par défaut, donc vous déclareriez cette fonction membre comme une fonction virtuelle pure. Cela indique ensuite à toute personne faisant une instance d'une classe dérivée, par exemple une classe Lion, qu'elle doit fournir une implémentation de la fonction membre makeSound qui fournira un rugissement d'une certaine manière.

Edit: Je oublié d'ajouter que c'est l'un des articles dans l'excellent livre de Scott Meyers « efficace C++ » (sanitised Amazon link) que je recommande fortement.

HTH

acclamations,

1

Répondre à la deuxième question (par comparaison, mais nous voulons transmettre un objet (classe autodéfinie) comment pouvons-nous mettre en œuvre?)

Si vous voulez pour utiliser votre propre classe dans une fonction de modèle en utilisant l'opérateur >. Votre classe n'a besoin que de définir cet opérateur ou cette fonction.

La partie importante est que votre classe doit définir la même l'opérateur ou la fonction que le modèle utilise.

/Tobias

0

modèle fournit le chemin de paramètrer sur CONNU AU TEMPS quantités COMPILE.Notez qu'il peut être un type (std :: vecteur contiendra uniquement des entiers), mais ils peuvent aussi être des valeurs:

template <int N, typename T > class MyType 

est à la fois sur un sans canevas entier et un type, et MyType < 2, int> sera un type différent de MyType < 3, int>.

En outre, les modèles permettent la métaprogrammation de modèles: c'est-à-dire que le compilateur exécute un programme au moment de la compilation. Il y a un exemple fascinant d'Erwin Unruh pour le calcul des nombres premiers au moment de la compilation.

Regardez http://ubiety.uwaterloo.ca/~tveldhui/papers/priority.html pour un petit peu d'histoire.

2

Dans l'exemple fourni, tout est OK tant que operator > est défini pour le type de données dont vous comparez les instances.

Par exemple, si vous définissez la classe suivante:

class fraction 
{ 
private: 
    int _num, _den; 

public: 
    fraction(int num, int den) 
    { 
     if (den >= 0) 
     { 
      _num = num; 
      _den = den; 
     } 
     else 
     { 
      _num = -num; 
      _den = -den; 
     } 
    } 

    fraction(const fraction &f) 
    { 
     _num = f._num; 
     _den = f._den; 
    } 

    bool operator > (const fraction &f) const 
    { 
     return (_num * f._den) > (f._num * _den); 
    } 

    bool operator == (const fraction &f) const 
    { 
     return (_num * f._den) == (f._num * _den); 
    } 
}; 

Ensuite, vous pouvez utiliser votre fonction de modèle avec des instances de cette classe.

int main(int argc, char* argv[]) 
{ 
    fraction a(1,2); // 0.5 
    fraction b(3,4); // 0.75 
    assert(GetMax/*<fraction>*/(a,b) == a); 
    return 0; 
} 
0

Il est important de noter qu'il est parfaitement normal de ne pas écrire vos propres modèles. Si vous n'êtes pas sûr de savoir pourquoi vous pourriez en avoir besoin, alors vous n'en avez probablement pas besoin. Les modèles sont un outil très puissant, mais ils ne sont pas toujours la meilleure solution.

Sur n'importe quelle plate-forme de développement commune, la bibliothèque standard fournit une implémentation de haute qualité de nombreuses utilisations traditionnelles des modèles à l'ancienne. L'utilisation des classes et des fonctions de bibliothèque standard ne nécessite pas l'écriture de nouveaux modèles. Par exemple, il fournit std :: max(), qui est le même que votre exemple.

Questions connexes