C ++: espacios de nombres: ¿cómo utilizarlos correctamente en los archivos fuente y de encabezado?

88

Considere un par de dos archivos fuente: un archivo de declaración de interfaz ( *.ho *.hpp) y su archivo de implementación ( *.cpp).

Deje que el *.harchivo sea como el siguiente:

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

He visto dos prácticas diferentes para usar espacios de nombres en archivos fuente:

*.cpp mostrando la práctica # 1:

#include "MyClass.h"
using namespace MyNamespace;

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

*.cpp mostrando la práctica # 2:

#include "MyClass.h"
namespace MyNamespace {

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

}

Mi pregunta: ¿Existen diferencias entre estas dos prácticas y se considera que una es mejor que la otra?

Nickolay
fuente
30
También existe la opción 3: solo usamos el nombre completo, por ejemplo int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Posible duplicado: stackoverflow.com/questions/7789163/…
David
@Dave no duplicar. Estas preguntas se complementan entre sí. Recomiende agregar el enlace proporcionado por Dave como "Leer también ..." a esta pregunta. Mi pregunta ayudará a los principiantes a elegir el estilo correcto.
nickolay
Posible duplicado: stackoverflow.com/questions/8210935/…
Firedragon

Respuestas:

62

Desde el punto de vista de la legibilidad del código, probablemente sea mejor, en mi opinión, usar el método # 2 por esta razón:

Puede haber usingvarios espacios de nombres a la vez, y cualquier objeto o función escrito debajo de esa línea puede pertenecer a cualquiera de esos espacios de nombres (salvo conflictos de nombres). Envolver todo el archivo en un namespacebloque es más explícito y le permite declarar nuevas funciones y variables que pertenecen a ese espacio de nombres dentro del archivo .cpp también

Dan F
fuente
La pregunta que Dave vinculó en su comentario a su pregunta también describe algunos puntos clave en las diferencias (si las hay) entre los dos métodos que está viendo
Dan F
Chicos, realmente no sé de quién elegir la respuesta. Tienen intersección mientras se complementan.
nickolay
Solo comentando para reconocer que algunos IDE como CLion solo detectarán implementaciones si usa la opción / práctica # 2.
PedroTanaka
@PedroTanaka, ¿sigue siendo así? No he notado tal problema.
John McFarlane
@JMcF No he verificado desde el momento en que publiqué el comentario. En las primeras versiones de Clion ocurría el problema.
PedroTanaka
51

La más clara es la opción que no mostró:

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

También es muy detallado; demasiado para la mayoría de la gente. Dado que using namespacees una receta para los conflictos de nombres, al menos en mi experiencia, y debe evitarse, excepto en ámbitos y lugares muy limitados, generalmente uso su # 2.

James Kanze
fuente
3
Gracias muy claro. Juntos creamos una buena página de preguntas frecuentes para los usuarios de espacios de nombres. :)
nickolay
2
Chicos, realmente no sé de quién elegir la respuesta. Tienen intersección mientras se complementan.
nickolay
10

¿Existen diferencias entre estas dos prácticas?

Si. # 1 y # 2 son ejemplos de una directiva using y una definición de espacio de nombres respectivamente. Son efectivamente iguales en este caso, pero tienen otras consecuencias. Por ejemplo, si introduce un nuevo identificador al lado MyClass::foo, tendrá un alcance diferente:

# 1:

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

# 2:

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

¿Se considera uno mejor que el otro?

Pros # 1: un poco más conciso; más difícil introducir accidentalmente algo enMyNamespace sin saberlo. Contras: puede extraer identificadores existentes de forma involuntaria.

# 2 Pros: es más claro que las definiciones de identificadores existentes y las declaraciones de identificadores nuevos pertenecen a ambas MyNamespace. Contras: es más fácil introducir identificadores de forma involuntaria MyNamespace.

Una crítica tanto del n. ° 1 como del n. ° 2 es que se refieren a un espacio de nombres completo cuando probablemente solo le interese la definición de miembros de MyNamespace::MyClass . Esto es torpe y comunica mal la intención.

Una posible alternativa al n. ° 1 es una declaración de uso que incluye solo el identificador que le interesa:

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

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

También me gustaría agregar que si, por alguna razón, decide implementar una especialización de plantilla en un archivo cpp y simplemente confía en using namespaceque se encontrará con el siguiente problema:

// .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(){}

De lo contrario, si aplica el método n. ° 2, estará bien.

Jordán
fuente
0

Me gustaría agregar una forma más, usando using-statement :

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

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

De esta manera, le ahorra escribir el nombre del espacio de nombres muchas veces si la clase tiene muchas funciones

Joanna
fuente