web-dev-qa-db-fra.com

Espaces de noms C ++, comparaison avec Java

J'ai fait un tas de Java codage récemment et je me suis habitué à des systèmes de nommage de packages très spécifiques, avec une imbrication profonde, par exemple com.company.project.db. Cela fonctionne bien en Java, AS3/Flex et C #. J'ai également vu le même paradigme appliqué en C++, mais j'ai également entendu qu'il est mauvais de voir les espaces de noms C++ comme des homologues directs des packages Java Java.

Est-ce vrai et pourquoi? En quoi les espaces de noms/packages sont-ils semblables et différents? Quels problèmes sont susceptibles d'être rencontrés si vous utilisez des espaces de noms imbriqués profonds?

45
Mr. Boy

En C++, les espaces de noms consistent simplement à partitionner les noms disponibles. Java concernent les modules. La hiérarchie de nommage n'en est qu'un aspect.

Il n'y a rien de mal, en soi, avec des espaces de noms profondément imbriqués en C++, sauf qu'ils ne sont normalement pas nécessaires car il n'y a pas de système de modules derrière eux, et les couches supplémentaires ajoutent simplement du bruit. Il est généralement suffisant d'avoir un ou deux niveaux d'espace de noms, avec le niveau supplémentaire impair pour les détails internes (souvent simplement appelés Détails).

Il existe également des règles supplémentaires pour les espaces de noms C++ qui peuvent vous surprendre en cas de surutilisation - telles que recherche dépendante de l'argument , et les règles relatives à la résolution aux niveaux parents. WRT ce dernier, prenez:

namespace a{ namespace b{ int x; } }
namespace b{ string x; }
namespace a
{
  b::x = 42;
}

Est-ce légal? Est-il évident ce qui se passe? Vous devez connaître la priorité de la résolution de l'espace de noms pour répondre à ces questions.

30
philsquared

Les packages Java ne sont pas imbriqués, ils sont plats. Toute imbrication apparente n'est rien de plus qu'une convention de dénomination.

Par exemple, le package com.company.project.db n'a aucun rapport avec com.company.project ou com.company.project.db.x. Code en com.company.project.db n'a plus accès au code dans com.company.project.db.x que ne coderait en a.b.c.

19
skaffman

Voici quelques raisons pour lesquelles et comment les espaces de noms C++ sont différents des espaces de noms Java ou C #.

Éviter les conflits

Dans les langages Java/C #, les espaces de noms sont destinés à éviter les conflits entre les noms dans différentes parties des bibliothèques de classes. Vous pouvez avoir une classe appelée "Watcher" à 5 endroits différents dans la hiérarchie des espaces de noms en C #. En C++, si vous avez les mêmes classes nommées dans votre bibliothèque, vous placez à l'intérieur d'une autre classe au lieu de créer des espaces de noms. De telles classes imbriquées sont toutes correctes et encouragées et en fait la syntaxe traite également comme si la classe est un espace de noms utilisant :: operator.

Combien d'espaces de noms imbriqués devraient être?

Les bibliothèques populaires comme Boost, Eigen et bien sûr, STL fournissent de bons exemples. ces bibliothèques contiennent généralement à peu près tout ce qui se trouve dans un espace de noms comme std:: ou boost:: ou eigen::. Peu de composants ont leur propre espace de noms comme std::ios ou boost:filesystem. Il n'y a pas de règles cohérentes sur le moment d'utiliser le deuxième niveau, mais il semble que les composants volumineux ou développés/maintenus séparément ou facultatifs obtiennent généralement leurs propres espaces de noms. Le troisième niveau est encore plus rare. En général, j'utilise la structure company::project et pour les grands sous-systèmes de projets utilisables indépendamment company::project::component.

Éviter les conflits dans les bibliothèques externes

Maintenant, la grande question: Et si vous obtenez deux bibliothèques de deux personnes différentes qui ont exactement les mêmes espaces de noms et classes? Cette situation est assez rare car la plupart des gens ont tendance à envelopper leurs bibliothèques au moins au nom de leur projet. Même si les noms de projet sont identiques, il est encore plus rare que vous finissiez par utiliser des bibliothèques des deux. Cependant, parfois, de mauvaises décisions sont prises pour les noms de projets (ahm ... "metro", "apollo" ...) ou même les espaces de noms ne sont tout simplement pas utilisés du tout. Si cela se produit, vous encapsulez #include pour l'une des bibliothèques ou les deux dans un espace de noms et le conflit est résolu! C'est une des raisons pour lesquelles les gens ne se soucient pas trop des conflits car les résoudre est trivial. Si vous suivez la pratique d'utilisation de company::project alors les conflits deviennent super rares.

Différences de langues

Bien que C++ fournisse using namespace, tout comme C #, son généralement considéré comme une mauvaise pratique pour "importer" tout dans votre propre espace de noms. La raison en est qu'un en-tête peut contenir beaucoup de "mauvaises" choses, y compris la redéfinition de choses qui peuvent totalement vous surprendre. C'est assez différent de C #/Java où vous n'obtenez une interface publique propre que lorsque vous faites l'équivalent de using namespace. (note latérale: en C++, vous pouvez réaliser la même chose en utilisant le modèle Pimpl mais c'est généralement trop de plomberie supplémentaire et peu de bibliothèques le font réellement). Donc, vous ne voulez presque jamais faire using namespace. À la place, vous faites typedefs (ou using name =) pour ce que vous voulez réellement utiliser. Cela rend à nouveau les espaces de noms profondément imbriqués peu pratiques à utiliser.

Code d'organisation

En Java/C #, les gens ont souvent tendance à organiser le code dans des dossiers. Généralement, à mesure que le dossier augmente de plus de 20 ou même 10 fichiers, les gens commencent à penser aux dossiers. En C++, les choses sont plus diversifiées mais pour de nombreux grands projets, des structures de répertoires plus plates sont préférées. Par exemple, la bibliothèque standard dossier std a 53 fichiers et le projet folly de Facebook semble suivre le même chemin. Je pense qu'une raison à cela est probablement le fait que les gens Java/C # utilisent plus les IDE visuels et utilisent les parchemins de la souris dans la navigation dans les dossiers plutôt que sur la console où vous pouvez utiliser des caractères génériques pour trouver un fichier dans une structure plate. De plus, les programmeurs C++ n'hésitent absolument pas à mettre plusieurs classes dans un seul fichier et à nommer le fichier comme unité logique au lieu de le même que le nom de classe contrairement à C # ou Java. Cela rend la compilation plus rapide, ce qui est très important pour les grands projets. Bien qu'il n'y ait aucune exigence au niveau de la langue pour avoir son propre espace de noms pour chaque dossier, de nombreux développeurs C++ préfèrent attribuer son propre espace de noms à chaque dossier et conserver la hiérarchie des dossiers à 2 niveaux ou moins.

Exception possible

En C++, vous pouvez vous référer à A::B::C::D comme on vient de C::D si vous êtes déjà à l'intérieur A::B. Donc, si vous avez du code privé ou des classes moins utilisées que vous souhaitez pousser plus loin, vous pouvez le faire tout en conservant votre propre profondeur relative à environ 2. Dans ce cas, vous pouvez également créer un dossier pour chaque niveau afin que les emplacements des fichiers soient prévisibles. En général, il n'y a pas de normes d'or dans ce domaine, mais vous ne voulez pas aller trop loin avec des espaces de noms profondément imbriqués imitant C #/Java.

Connexes

6
Shital Shah

Je pense qu'il manque quelque chose dans les réponses précédentes, et c'est l'une des raisons pour lesquelles j'aime vraiment C++.

Imaginez que vous programmez une application graphique et soudain vous vous rendez compte qu'il y a quelque chose de commun entre tous vos widgets. Vous voulez que tous aient une nouvelle fonction. Que faire?

1) Modifier la classe de widget de base? OK, mais vous n'y avez probablement pas accès. Il y a peut-être un problème de licence qui vous empêche de faire votre propre modification. Même si vous pouvez le faire, si c'est quelque chose qui n'a de sens que pour votre projet, les auteurs ne l'incluront pas dans leur future version, et la mise à niveau de la boîte à outils sera plus douloureuse

2) Créer une classe d'interface/multi-héritage? En fonction de votre code existant, il sera plus ou moins difficile de mettre à jour chaque classe associée à un widget. Faites cela et votre code coûtera plus cher à maintenir car tout le monde définissant une nouvelle classe doit savoir qu'ils sont censés hériter de votre interface. Devenir dépendant de la discipline d'autrui est vraiment risqué.

La chose merveilleuse des espaces de noms C++ ici est que vous avez un moyen supplémentaire d'encapsuler stuff dans un système déjà existant. Non seulement vous pouvez encapsuler dans des bibliothèques déjà existantes que vous ne pouvez pas modifier, vous pouvez encapsuler des concepts similaires que vous ne pouvez pas facilement insérer dans votre hiérarchie de classes/objets.

Java vous oblige à vous concentrer davantage sur une conception OOP pure. Bien sûr, je dis que vous pourriez être un hack sale et pas élégant, mais il y a beaucoup de programmeurs paresseux qui ne passent pas de temps à réparer leurs conceptions.

0
SystematicFrank

Vous pouvez avoir des espaces de noms imbriqués en C++.

Cependant, ils ne fonctionnent pas de la même manière qu'en Java, évidemment. Java sont vraiment bien mieux définis et il n'y a pas de véritable étrangeté init statique. Avec les espaces de noms C++ sont utiles mais comportent toujours pas mal de danger.

0
Charles Eli Cheese

En C++, l'unité de base de conception et d'implémentation est la classe, pas l'espace de noms. Les espaces de noms étaient destinés à empêcher les conflits de noms dans les grandes bibliothèques, et non à exprimer des concepts.

Les classes ont plusieurs avantages par rapport aux espaces de noms:

  • ils peuvent avoir des constructeurs et des destructeurs
  • ils peuvent avoir des membres privés
  • ils ne peuvent pas être rouverts

Cependant, j'examinerais deux fois toute relation profondément imbriquée. Ce n'est vraiment pas un bon moyen de concevoir un logiciel, et conduit à du code illisible, que ce soit des classes ou des espaces de noms.

0
anon

Je vais ajouter quelques éléments que j'ai entendus, mais je ne connais pas la véracité de, veuillez aider à les confirmer/les dissiper.

  1. Les performances C++ sont (en quelque sorte) affectées par des noms de méthode longs et entièrement spécifiés, par exemple namespace1::namespace2::namespace3::classX::method123()

  2. Vous pouvez atteindre une limite sur la longueur de symbole autorisée dans le compilateur/éditeur de liens

0
Mr. Boy