C ++: Namespaces - Comment utiliser correctement dans les fichiers d'en-tête et source?

88

Considérez une paire de deux fichiers source: un fichier de déclaration d'interface ( *.hou *.hpp) et son fichier d'implémentation ( *.cpp).

Laissez le *.hfichier ressembler à ce qui suit:

namespace MyNamespace {
  class MyClass {
  public:
    int foo();
  };
}

J'ai vu deux pratiques différentes pour utiliser les espaces de noms dans les fichiers source:

*.cpp montrant la pratique n ° 1:

#include "MyClass.h"
using namespace MyNamespace;

int MyClass::foo() { ... }

*.cpp montrant la pratique n ° 2:

#include "MyClass.h"
namespace MyNamespace {

  int MyClass::foo() { ... }

}

Ma question: y a-t-il des différences entre ces deux pratiques et l'une est-elle considérée comme meilleure que l'autre?

Nickolay
la source
30
Il y a aussi l'option 3: juste nous le nom complet, par exemple int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Double possible: stackoverflow.com/questions/7789163/…
David
@Dave pas dupliqué. Ces questions se complètent. Il est recommandé d'ajouter le lien fourni par Dave comme "Lire aussi ..." à cette question. Ma question aidera les novices à choisir le bon style.
nickolay
Double

Réponses:

62

Du point de vue de la lisibilité du code, il est probablement préférable à mon avis d'utiliser la méthode n ° 2 pour cette raison:

Vous pouvez avoir usingplusieurs espaces de noms à la fois, et tout objet ou fonction écrit sous cette ligne peut appartenir à l'un de ces espaces de noms (sauf conflits de noms). L'emballage du fichier entier dans un namespacebloc est plus explicite et vous permet de déclarer de nouvelles fonctions et variables qui appartiennent à cet espace de noms dans le fichier .cpp également

Dan F
la source
La question que Dave a liée dans son commentaire à votre question décrit également certains points clés des différences (le cas échéant) entre les deux méthodes que vous examinez
Dan F
Les gars, je ne sais vraiment pas à qui choisir la réponse. Ils ont une intersection tout en se complétant.
nickolay
Il suffit de commenter pour reconnaître que certains IDE comme CLion ne détecteront les implémentations que si vous utilisez l'option / practice # 2.
PedroTanaka
@PedroTanaka est-ce toujours le cas? Je n'ai pas remarqué un tel problème.
John McFarlane
@JMcF Je n'ai pas vérifié depuis le moment où j'ai publié le commentaire. Dans les premières versions de Clion, le problème est survenu.
PedroTanaka le
51

Le plus clair est l'option que vous n'avez pas montrée:

int MyNamespace::MyClass::foo()
{
    //  ...
}

C'est aussi très verbeux; trop pour la plupart des gens. Puisqu'il using namespaces'agit d'une réception pour les conflits de noms, du moins d'après mon expérience, et qu'elle doit être évitée sauf dans des domaines et des endroits très limités, j'utilise généralement votre # 2.

James Kanze
la source
3
Merci très clairement. Ensemble, nous avons créé une bonne page de FAQ pour les utilisateurs d'espaces de noms. :)
nickolay
2
Les gars, je ne sais vraiment pas à qui choisir la réponse. Ils ont une intersection tout en se complétant.
nickolay
10

Y a-t-il des différences entre ces deux pratiques

Oui. # 1 et # 2 sont respectivement des exemples de directive using et de définition d'espace de noms . Ils sont effectivement les mêmes dans ce cas mais ont d'autres conséquences. Par exemple, si vous introduisez un nouvel identifiant à côté MyClass::foo, il aura une portée différente:

#1:

using namespace MyNamespace;
int x;  // defines ::x

# 2:

namespace MyNamespace {
  int x;  // defines MyNamespace::x
}

l'un est-il considéré comme meilleur que l'autre?

# 1 Avantages: un peu plus concis; plus difficile d'introduire accidentellement quelque chose MyNamespacesans le vouloir. Inconvénients: peut extraire des identifiants existants sans le vouloir.

# 2 Pour: il est plus clair que les définitions d'identifiants existants et les déclarations de nouveaux identifiants appartiennent à la fois MyNamespace. Inconvénients: il est plus facile d'introduire involontairement des identifiants MyNamespace.

Une critique à la fois des # 1 et # 2 est qu'ils font référence à un espace de noms entier alors que vous ne vous souciez probablement que de la définition des membres de MyNamespace::MyClass. C'est lourd et cela communique mal l'intention.

Une alternative possible à # 1 est une déclaration d'utilisation qui inclut uniquement l'identifiant qui vous intéresse:

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }
John McFarlane
la source
4

Je voudrais également ajouter que si vous décidez pour une raison quelconque d'implémenter une spécialisation de modèle dans un fichier cpp et que vous comptez simplement sur using namespacevous, vous rencontrerez le problème suivant:

// .h file
namespace someNameSpace
{
  template<typename T>
    class Demo
    {
      void foo();
    };
}

// .cpp file
using namespace someNameSpace;

template<typename T>
void Demo<T>::foo(){}

// this will produce
// error: specialization of 'template<class T> void someNameSpace::Demo<T>::foo()' in different namespace [-fpermissive]
template<>
void Demo<int>::foo(){}

Sinon, si vous appliquez la méthode n ° 2, tout ira bien.

Jordan
la source
0

Je voudrais ajouter une autre manière, en utilisant using-declaration :

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

Cette façon vous évite de taper plusieurs fois le nom de l'espace de noms si la classe a de nombreuses fonctions

Joanna
la source