2010-11-30 29 views
7

J'ai une classe Foo qui est utilisée dans un petit projet autonome. Il a une définition de classe dans Foo.h avec l'implémentation des fonctions membres de la classe dans un fichier d'implémentation Foo.cpp.Où définir la fonction de modèle de membre de classe C++ et les foncteurs qui l'instancient?

Première question - l'une des fonctions membres de la classe Foo est une méthode template Foo :: doSomething(), est-il correct que l'implémentation de cette méthode apparaisse avec la déclaration de la fonction dans Foo.h?

Le paramètre de modèle dont Foo :: doSomething() sera instancié avec est l'un des deux types de Functor - classe CalcA et CalcB.

Dois-je:

  • (A) mettre la défintion et la mise en œuvre des deux classes de foncteurs tous ensemble dans foo.cpp (où ils sont utilisés par la mise en œuvre d'autres fonctions membres Foo pour appeler Foo: :faire quelque chose).
  • (B) mettre la définition et l'implémentation des deux classes Functor dans Foo.h.
  • (C) dois-je mettre divisé la définition et l'implémentation des deux Functors à travers Foo.h et Foo.cpp comme cela serait fait avec une classe ordinaire?

Répondre

7

Règle générale:

Si foo :: doSomething() est utilisée en dehors foo.cpp (à savoir si elle est publique ou protégée, en général), il faut aller dans l'en-tête.

Si ce n'est pas le cas, mettre dans le fichier cpp est parfaitement ok, et même une bonne idée (car il éloigne le fouillis du fichier d'en-tête). Donc, si les foncteurs sont seulement utilisés dans le fichier cpp, mettez la fonction template là aussi. On peut toujours refactoriser les choses plus tard si cela change.

+1

Dans le cas où un modèle est nécessaire dans un seul fichier CPP, alors je mettrais la déclaration et la définition dans ce fichier. S'il s'agit d'un modèle de fonction membre et que le reste de la classe est utilisé dans d'autres unités de traduction, je mettrai certainement la définition dans le fichier .h. Je serais même préoccupé par le comportement indéfini ou défini par l'implémentation si cela n'était pas fait. –

+0

@ Marcus, j'aurais dû mentionner que dans la question d'origine - oui Foo :: doSomething() est une fonction membre de modèle privé de la classe Foo. –

+1

@John, je ne suis pas votre point, pourriez-vous élaborer un peu? Dans mon cas, j'ai une fonction de membre de modèle privé de la classe Foo, donc elle est seulement utilisée par l'implémentation de la classe Foo dans Foo.cpp. Cela semble donc correspondre au premier cas/phrase dans votre commentaire. Mais ma situation correspond également à la description dans la deuxième phrase de votre commentaire ("modèle de fonction membre et le reste de la classe est utilisé dans d'autres unités de traduction") où vous dites de le mettre dans un fichier .h. –

1

Mon défaut serait de mettre la définition des modèles de fonction de membre droit dans le fichier .h, comme ceci:

class Foo 
{ 
public: 
    template<typename T> void DoSomething(T t); 
}; 

// ... later... 

template<typename T> 
void Foo::DoSomething(T t) 
{ 
    // ... 
} 

Si cela est suboptimale pour un cas particulier, je prendrais des mesures plus héroïques. En commençant par #include en ajoutant un fichier .inc avec la définition à la fin du fichier .h, ou éventuellement en effectuant des instanciations explicites dans les fichiers .cpp où j'avais besoin des modèles de fonction membre à utiliser.

+0

IMHO c'est surdimensionner un peu, si ce n'est qu'une seule fonction. Mais c'est un sujet bikeshed de toute façon. :) – Macke

+0

@Marcus: Qu'est-ce que c'est précisément la sur-conception? –

+0

@Marcus: Je pense que je vois ce que vous dites maintenant –

1

La définition de la méthode de gabarit doit en effet se trouver dans le fichier d'en-tête de la classe à laquelle elle appartient.

Comme ceci:

class MyClass 
{ 
    template <typename T> 
    void foo(const T&) 
    { 
     // Definition 
    } 
}; 

Ou comme ceci (notez que la définition de la méthode de modèle peut être inclus de fichier séparé après la déclaration de classe)

class MyClass 
{ 
    template <typename T> void foo(const T&); 
}; 

template <typename T> 
void MyClass::foo(const T&) 
{ 
    // Definition 
} 

Le reste est dépend du style que vous convenu et vos besoins.

Je mettrais la déclaration de functor (ou même la définition si elles sont simples) dans l'en-tête si je les utilise non seulement dans Foo ou si Foo les a comme membre de classe.

7

D'abord, vous devez comprendre le mécanisme des modèles.Les modèles ne sont pas compilés, ils sont instanciés lorsqu'ils sont utilisés et ensuite leur instanciation est compilée. Le compilateur doit donc avoir la définition complète du modèle dans chaque module en utilisant la fonction template, afin de les instancier d'abord en fonction des paramètres que vous avez passés.

Pour résoudre votre problème, il existe trois solutions, mais vous verrez qu'elles aboutissent au même résultat. Soit vous mettre en œuvre vos modèles entiers dans votre fichier d'en-tête dans la définition de la classe (nous utilisons pour les suffixe avec .hxx au lieu de .h afin de préciser qu'ils contenant des définitions de modèles):

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 

class Foo { 
    public: 
    template <class T>  
    void bar(const T& t) { 
     t.doSomething(); 
    } 
}; 
#endif 

Ou vous pouvez la définition de extérioriser la classe, mais toujours dans le fichier d'en-tête:

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 

class Foo { 
    public: 
     template <class T>  
     void bar(const T&); 
}; 

template <class T> 
void Foo::bar(const T& t) { 
    t.doSomething(); 
} 
#endif 

Enfin, vous pouvez mettre en œuvre des organismes de méthodes de modèle dans un fichier externe (préfixés par .cxx pour la même raison). Il contiendra les corps des méthodes mais n'inclura pas "Foo.hxx". Au lieu de cela, c'est "Foo.hxx" qui inclura "Foo.cxx" après la définition de la classe. De cette façon, lorsque le compilateur résout la directive #include, il trouve la définition entière du modèle dans le même module, ce qui lui permet instancier:

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 
class Foo { 
    public: 
     template <class T>  
     void bar(const T&); 
}; 

#include "Foo.cxx" 

#endif 

// Foo.cxx 
template <class T> 
void Foo::bar(const T& t) { 
    t.doSomething(); 
} 

Le choix entre ces 3 façons de mettre en œuvre des modèles est plutôt une question de lisibilité (et goût).
Deuxième et troisième sont équivalents en termes de code généré, mais je préfère ne pas utiliser la solution de fichier cxx, car elle conduit souvent à des erreurs stupides lorsque vous oubliez d'inverser l'include. En outre, les bibliothèques C++ bien connues comme STL ou Boost proposent leur code uniquement dans les fichiers d'en-tête, ce qui est un signe de bon design. En utilisant la définition externe dans les en-têtes, vous clarifiez la définition de votre classe. Vous empêchez également le compilateur à automatiquement les méthodes en ligne, ce qui peut parfois conduire à des résultats médiocres selon Herb Sutter http://www.gotw.ca/gotw/033.htm

+0

merci pour la réponse. J'aurais dû mentionner dans ma question initiale que la fonction de membre modèle dans ma situation est une fonction de membre privé de la classe Foo. Séparément de cela - étant donné les trois suggestions dans votre réponse quels sont les avantages/inconvénients de chacun? La principale différence que je peux voir est que dans votre première suggestion, la fonction de membre serait implicitement inline et dans les deux autres, non, n'est-ce pas? Quels sont les avantages/désavantages des deuxième et troisième par rapport aux autres? –

+1

@Czarak: vous avez raison -> les méthodes définies dans un corps de classe sont automatiquement insérées (voir http://stackoverflow.com/questions/1443982/when-do-compilers-inline-c-code pour plus de détails sur l'inlining) . J'ai édité ma réponse pour ajouter les précisions que vous avez demandées. J'espère que ça t'a aidé. – jopasserat