Disons que j'ai, ou vais écrire, un ensemble de fonctions connexes. Disons qu'ils sont liés aux mathématiques. Sur le plan organisationnel, devrais-je:
MyMath
et faites-y référence via MyMath::XYZ()
MyMath
et rendez ces méthodes statiques et faites référence à la même manière MyMath::XYZ()
Pourquoi devrais-je choisir l'un sur l'autre comme moyen d'organiser mon logiciel?
Par défaut, utilisez les fonctions namespaced.
Les classes consistent à construire des objets et non à remplacer des espaces de noms.
Scott Meyers a rédigé un article complet pour son livre Effective C++ sur ce sujet, "Préférer les fonctions non membres non amis aux fonctions membres". J'ai trouvé une référence en ligne à ce principe dans un article de Herb Sutter: http://www.gotw.ca/gotw/084.htm
La chose importante à savoir est que: En C++, les fonctions situées dans le même espace de noms qu'une classe appartiennent à l'interface de cette classe (parce que ADL recherchera ces fonctions lors de la résolution des appels de fonctions).
Les fonctions namespaced, à moins d'être déclarées "ami", n'ont pas accès aux éléments internes de la classe, contrairement aux méthodes statiques.
Cela signifie, par exemple, que lors de la gestion de votre classe, si vous devez modifier les éléments internes de votre classe, vous devez rechercher des effets secondaires dans toutes ses méthodes, y compris les méthodes statiques.
Ajout de code à une interface de classe.
En C #, vous pouvez ajouter des méthodes à une classe même si vous n'y avez pas accès. Mais en C++, c'est impossible.
Mais, toujours en C++, vous pouvez toujours ajouter une fonction namespaced, même à une classe écrite par quelqu'un.
Voyez de l’autre côté, c’est important lors de la conception de votre code, car en plaçant vos fonctions dans un espace de noms, vous autoriserez vos utilisateurs à augmenter/compléter l’interface de la classe.
Effet secondaire du point précédent, il est impossible de déclarer des méthodes statiques dans plusieurs en-têtes. Toutes les méthodes doivent être déclarées dans la même classe.
Pour les espaces de noms, les fonctions du même espace de noms peuvent être déclarées dans plusieurs en-têtes (la fonction swap presque standard en est le meilleur exemple).
Le cooless de base d'un espace de noms est que dans certains codes, vous pouvez éviter de le mentionner, si vous utilisez le mot clé "using":
#include <string>
#include <vector>
// Etc.
{
using namespace std ;
// Now, everything from std is accessible without qualification
string s ; // Ok
vector v ; // Ok
}
string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR
Et vous pouvez même limiter la "pollution" à une classe:
#include <string>
#include <vector>
{
using std::string ;
string s ; // Ok
vector v ; // COMPILATION ERROR
}
string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR
Ce "modèle" est obligatoire pour une utilisation correcte de l’idiome de swap presque standard.
Et cela est impossible à faire avec des méthodes statiques dans les classes.
Ainsi, les espaces de noms C++ ont leur propre sémantique.
Mais cela va plus loin, car vous pouvez combiner des espaces de noms de manière similaire à l'héritage.
Par exemple, si vous avez un espace de noms A avec une fonction AAA, un espace de noms B avec une fonction BBB, vous pouvez déclarer un espace de noms C et amener AAA et BBB dans cet espace de noms avec le mot-clé using.
Les espaces de noms sont pour les espaces de noms . Les classes sont pour les classes.
C++ a été conçu pour que chaque concept soit différent et soit utilisé différemment, dans différents cas, comme solution à différents problèmes.
N'utilisez pas de classes lorsque vous avez besoin d'espaces de noms.
Et dans votre cas, vous avez besoin d'espaces de noms.
Il y a beaucoup de gens qui seraient en désaccord avec moi, mais voici comment je vois les choses:
Une classe est essentiellement une définition d'un certain type d'objet. Les méthodes statiques doivent définir des opérations intimement liées à cette définition d'objet.
Si vous allez simplement avoir un groupe de fonctions connexes non associées à un objet sous-jacent ou à la définition d'un type d'objet , alors je dirais que vous n'utilisez qu'un espace de noms. Juste pour moi, conceptuellement, c'est beaucoup plus sensé.
Par exemple, dans votre cas, demandez-vous: "Qu'est-ce qu'un MyMath?" Si MyMath
ne définit pas un type d'objet, alorsIdirait: n'en faites pas une classe.
Mais comme je l'ai dit, je sais qu'il y a beaucoup de gens qui seraient (même avec véhémence) en désaccord avec moi à ce sujet (notamment les développeurs Java et C #).
Sinon, utilisez les fonctions namespaced.
En réponse aux commentaires: oui, les méthodes statiques et les données statiques ont tendance à être surexploitées. C'est pourquoi je n'ai proposé que deux scénarios liés dans lesquels je pense qu'ils peuvent être utiles. Dans l'exemple spécifique du PO (un ensemble de routines mathématiques), s'il souhaitait pouvoir spécifier des paramètres - disons, un type de données de base et une précision de sortie - qui seraient appliqués à toutes les routines, il pourrait faire quelque chose comme:
template<typename T, int decimalPlaces>
class MyMath
{
// routines operate on datatype T, preserving at least decimalPlaces precision
};
// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;
Si vous n'en avez pas besoin, alors à tout prix utilisez un espace de noms.
Vous devez utiliser un espace de noms, car un espace de noms présente de nombreux avantages par rapport à une classe:
using
un membre de la classe; vous pouvez using
un membre d'espace de nomusing class
, bien que using namespace
ne soit pas si souvent une bonne idéeLes membres statiques sont, à mon avis, très très surutilisés. Ils ne sont pas vraiment nécessaires dans la plupart des cas. Les fonctions membres statiques sont probablement plus avantageuses que les fonctions de fichier, et les membres données statiques sont simplement des objets globaux avec une meilleure réputation non méritée.
Je préférerais les espaces de noms, de cette façon, vous pouvez avoir des données privées dans un espace de noms anonyme dans le fichier d'implémentation (de sorte qu'il ne soit pas nécessaire qu'il apparaisse dans l'en-tête, contrairement aux membres private
). Un autre avantage est que, grâce à using
votre espace de nom, les clients des méthodes peuvent choisir de ne pas spécifier MyMath::
.
L'espace de noms et la méthode de classe ont leurs utilisations. Les espaces de noms peuvent être répartis sur plusieurs fichiers. Cependant, cela constitue un point faible si vous devez appliquer tout le code associé à un seul fichier. Comme mentionné ci-dessus, la classe vous permet également de créer des membres statiques privés dans la classe. Vous pouvez l'avoir dans l'espace de noms anonyme du fichier d'implémentation, mais sa portée est encore plus grande que celle de les avoir à l'intérieur de la classe.
Une raison de plus d'utiliser class - Option permettant d'utiliser des spécificateurs d'accès. Vous pouvez ensuite éventuellement diviser votre méthode statique publique en méthodes privées plus petites. Les méthodes publiques peuvent appeler plusieurs méthodes privées.