¿Cómo se usan correctamente los espacios de nombres en C ++?

231

Vengo de un fondo de Java, donde se usan paquetes, no espacios de nombres. Estoy acostumbrado a poner clases que funcionan juntas para formar un objeto completo en paquetes, y luego reutilizarlas más tarde de ese paquete. Pero ahora estoy trabajando en C ++.

¿Cómo se usan los espacios de nombres en C ++? ¿Crea un único espacio de nombres para toda la aplicación o crea espacios de nombres para los componentes principales? Si es así, ¿cómo se crean objetos a partir de clases en otros espacios de nombres?

Mario
fuente

Respuestas:

167

Los espacios de nombres son paquetes esencialmente. Se pueden usar así:

namespace MyNamespace
{
  class MyClass
  {
  };
}

Luego en código:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

O, si desea utilizar siempre un espacio de nombres específico, puede hacer esto:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

Editar: Siguiendo lo que bernhardrusch ha dicho , tiendo a no usar la sintaxis "usar el espacio de nombres x", generalmente especifico explícitamente el espacio de nombres al instanciar mis objetos (es decir, el primer ejemplo que mostré).

Y como solicitó a continuación , puede usar tantos espacios de nombres como desee.

Mark Ingram
fuente
25
En mi opinión, es mejor acostumbrarse a prefijar el stdespacio de nombres a los símbolos en lugar de usarlos using. Entonces siempre escribo std::couto std::stringahora porque así es como los llamo ahora. Nunca solo escribiría cout.
Tom Savage el
55
Si bien esto es muy cierto std, personalmente he encontrado que esto es mucho menos importante cuando se trata de bibliotecas más pequeñas. A menudo solo puede usar using namespace FooBario;, especialmente si está utilizando una cantidad considerable de tipos de una biblioteca.
jkerian
44
@ jkerian, entiendo tu punto de vista, pero no estoy de acuerdo porque las colisiones de nombres (en mi opinión) son más propensas a provenir precisamente de bibliotecas tan pequeñas. La mayoría de las personas tienen cuidado de no nombrar clases / funciones de la misma manera que en STL. Dicho esto, estoy de acuerdo en que using namespace X;debe evitarse en los archivos de encabezado si es posible.
Alan Turing
12
@LexFridman "La mayoría de las personas tienen cuidado de no nombrar las clases / funciones de la misma manera que las de STL", eso NO ES VERDADERO. Por ejemplo, si tuviera que escribir un código de E / S muy especializado para algún hardware extraño, nunca, nunca, usaría otra cosa que no fuera mylibrary::endlpara representar mi propia secuencia especial de nueva línea. Quiero decir, ¿por qué inventar nombres?
Mi compilador aún no reconocerá el espacio de nombres, aunque quiero especificarlo explícitamente e incluir el archivo donde está declarado.
bgenchel
116

Para evitar decir todo, Mark Ingram ya dijo un pequeño consejo para usar espacios de nombres:

Evite la directiva "usar el espacio de nombres" en los archivos de encabezado; esto abre el espacio de nombres para todas las partes del programa que importan este archivo de encabezado. En los archivos de implementación (* .cpp) esto normalmente no es un gran problema, aunque prefiero usar la directiva "using namespace" en el nivel de función.

Creo que los espacios de nombres se utilizan principalmente para evitar conflictos de nombres, no necesariamente para organizar la estructura de su código. Organizaría programas C ++ principalmente con archivos de encabezado / estructura de archivos.

A veces, los espacios de nombres se utilizan en proyectos C ++ más grandes para ocultar detalles de implementación.

Nota adicional a la directiva de uso: Algunas personas prefieren usar "usar" solo para elementos individuales:

using std::cout;  
using std::endl;
bernhardrusch
fuente
2
Una ventaja de "usar el espacio de nombres" en el nivel de función como sugiere en lugar de en el nivel de archivo .cpp o el nivel de bloque de espacio de nombres {} dentro del .cpp es que ayuda mucho con las compilaciones de unidades de compilación única. "usar el espacio de nombres" es transitivo, y se aplica para el espacio de nombres A a través de bloques de espacio de nombres discretos A {} en la misma unidad, por lo que para las compilaciones de unidades de compilación simple terminas rápidamente usando todo si se hacen a nivel de bloque de espacio de nombres o archivos.
idij
using std::cout; es una declaración de uso
Konstantin
3
¿Es posible usar varios nombres de un solo espacio de nombres en una sola declaración? Algo así using std::cout, std::endl;o incluso using std::cout, endl;,.
AlQuemist
Puede estar bien usar un using namespace xen un encabezado si está dentro de otro espacio de nombres. No es algo que recomendaría en general, pero no contamina el espacio de nombres global.
Praxeolitic
79

Vincent Robert tiene razón en su comentario ¿Cómo se usan correctamente los espacios de nombres en C ++? .

Usando espacio de nombres

Los espacios de nombres se utilizan como mínimo para ayudar a evitar la colisión de nombres. En Java, esto se aplica a través del idioma "org.domain" (porque se supone que uno no usará nada más que su propio nombre de dominio).

En C ++, puede asignar un espacio de nombres a todo el código de su módulo. Por ejemplo, para un módulo MyModule.dll, puede asignar a su código el espacio de nombres MyModule. He visto en otra parte a alguien que usa MyCompany :: MyProject :: MyModule. Supongo que esto es excesivo, pero en general, me parece correcto.

Usar "usar"

El uso debe usarse con mucho cuidado porque importa efectivamente uno (o todos) los símbolos de un espacio de nombres a su espacio de nombres actual.

Es malo hacerlo en un archivo de encabezado porque su encabezado contaminará todas las fuentes incluidas (me recuerda a las macros ...), e incluso en un archivo de origen, mal estilo fuera del alcance de una función porque se importará a alcance global Los símbolos del espacio de nombres.

La forma más segura de usar "usar" es importar símbolos seleccionados:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

Verá mucho "usar el espacio de nombres estándar"; en tutoriales o códigos de ejemplo. La razón es reducir la cantidad de símbolos para facilitar la lectura, no porque sea una buena idea.

"utilizando el espacio de nombres estándar;" Scott Meyers lo desalienta (no recuerdo exactamente qué libro, pero puedo encontrarlo si es necesario).

Composición del espacio de nombres

Los espacios de nombres son más que paquetes. Otro ejemplo se puede encontrar en "El lenguaje de programación C ++" de Bjarne Stroustrup.

En la "Edición especial", en 8.2.8 Composición del espacio de nombres , describe cómo puede combinar dos espacios de nombres AAA y BBB en otro llamado CCC. Por lo tanto, CCC se convierte en un alias para AAA y BBB:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

Incluso podría importar símbolos seleccionados de diferentes espacios de nombres, para construir su propia interfaz de espacio de nombres personalizada. Todavía tengo que encontrar un uso práctico de esto, pero en teoría, es genial.

paercebal
fuente
¿Podría aclarar, por favor, "dé un espacio de nombres a todo el código en su módulo"? Lo que es una buena práctica encapsular al módulo. Por ejemplo, tengo una clase de números complejos y funciones externas relacionadas con números complejos. ¿Esta clase y esas dos funciones deberían estar en un espacio de nombres?
yanpas
74

No vi ninguna mención de ello en las otras respuestas, así que aquí están mis 2 centavos canadienses:

En el tema "uso del espacio de nombres", una declaración útil es el alias del espacio de nombres, que le permite "renombrar" un espacio de nombres, normalmente para darle un nombre más corto. Por ejemplo, en lugar de:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

puedes escribir:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;
Éric Malenfant
fuente
55

No escuche a todas las personas que le dicen que los espacios de nombres son solo espacios de nombres.

Son importantes porque el compilador considera que aplican el principio de interfaz. Básicamente, se puede explicar con un ejemplo:

namespace ns {

class A
{
};

void print(A a)
{
}

}

Si quisieras imprimir un objeto A, el código sería este:

ns::A a;
print(a);

Tenga en cuenta que no mencionamos explícitamente el espacio de nombres al llamar a la función. Este es el principio de la interfaz: C ++ considera una función que toma un tipo como argumento como parte de la interfaz para ese tipo, por lo que no es necesario especificar el espacio de nombres porque el parámetro ya implica el espacio de nombres.

Ahora, ¿por qué es importante este principio? Imagine que el autor de la clase A no proporcionó una función print () para esta clase. Tendrá que proporcionar uno usted mismo. Como es un buen programador, definirá esta función en su propio espacio de nombres, o tal vez en el espacio de nombres global.

namespace ns {

class A
{
};

}

void print(A a)
{
}

Y su código puede comenzar a llamar a la función print (a) donde lo desee. Ahora imagine que años más tarde, el autor decide proporcionar una función print (), mejor que la suya porque conoce los aspectos internos de su clase y puede hacer una versión mejor que la suya.

Luego, los autores de C ++ decidieron que su versión de la función print () debería usarse en lugar de la proporcionada en otro espacio de nombres, para respetar el principio de la interfaz. Y que esta "actualización" de la función print () debería ser lo más fácil posible, lo que significa que no tendrá que cambiar cada llamada a la función print (). Es por eso que las "funciones de interfaz" (función en el mismo espacio de nombres que una clase) pueden llamarse sin especificar el espacio de nombres en C ++.

Y es por eso que debe considerar un espacio de nombres C ++ como una "interfaz" cuando usa uno y tenga en cuenta el principio de la interfaz.

Si desea una mejor explicación de este comportamiento, puede consultar el libro Exceptional C ++ de Herb Sutter

Vincent Robert
fuente
23
En realidad, debe cambiar cada llamada a print () si se agrega ns :: Print, pero el compilador marcará cada llamada como ambigua. Cambiar silenciosamente a la nueva función sería una idea terrible.
Eclipse
Ahora me pregunto, teniendo lo que @Vincent ha dicho que tendrá que cambiar todas las llamadas para imprimir, si el autor proporcionaría la función ns :: Print (), ¿qué estaba tratando de decir? ¿Que cuando el autor agregó una función ns :: Print (), puede eliminar su propia implementación? ¿O que simplemente agregará usando ns :: print () usando-declaración? ¿O algo más? Gracias
Vaska el gato
36

Los proyectos más grandes de C ++ que he visto apenas utilizan más de un espacio de nombres (por ejemplo, biblioteca de impulso)

En realidad, boost utiliza toneladas de espacios de nombres, por lo general, cada parte de boost tiene su propio espacio de nombres para el funcionamiento interno y luego puede colocar solo la interfaz pública en el impulso de espacio de nombres de nivel superior.

Personalmente, creo que cuanto más grande se hace una base de código, los espacios de nombres más importantes se vuelven, incluso dentro de una sola aplicación (o biblioteca). En el trabajo colocamos cada módulo de nuestra aplicación en su propio espacio de nombres.

Otro uso (sin juego de palabras) de espacios de nombres que uso mucho es el espacio de nombres anónimo:

namespace {
  const int CONSTANT = 42;
}

Esto es básicamente lo mismo que:

static const int CONSTANT = 42;

Sin embargo, el uso de un espacio de nombres anónimo (en lugar de estático) es la forma recomendada para que el código y los datos sean visibles solo dentro de la unidad de compilación actual en C ++.


fuente
13
Ambos ejemplos son equivalentes a const int CONSTANT = 42;porque la constante de nivel superior en un ámbito de espacio de nombres ya implica una vinculación interna. Por lo tanto, no necesita el espacio de nombres anónimo en este caso.
sellibitze
19

Además, tenga en cuenta que puede agregar a un espacio de nombres. Esto es más claro con un ejemplo, lo que quiero decir es que puedes tener:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

en un archivo square.hy

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

en un archivo cube.h. Esto define un único espacio de nombres MyNamespace(es decir, puede definir un único espacio de nombres en varios archivos).

OysterD
fuente
11

En Java:

package somepackage;
class SomeClass {}

En C ++:

namespace somenamespace {
    class SomeClass {}
}

Y usándolos, Java:

import somepackage;

Y C ++:

using namespace somenamespace;

Además, los nombres completos son "somepackge.SomeClass" para Java y "somenamespace :: SomeClass" para C ++. Con esas convenciones, puede organizarse como está acostumbrado en Java, incluida la creación de nombres de carpetas coincidentes para espacios de nombres. Sin embargo, los requisitos de carpeta-> paquete y archivo-> clase no están allí, por lo que puede nombrar sus carpetas y clases independientemente de los paquetes y espacios de nombres.

Staale
fuente
6

@ marius

Sí, puede usar varios espacios de nombres a la vez, por ejemplo:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[Feb. 2014 - (¿Realmente ha pasado tanto tiempo?): Este ejemplo en particular ahora es ambiguo, como señala Joey a continuación. Boost y std :: ahora cada uno tiene un shared_ptr.]

Adam Hollidge
fuente
2
Tenga en cuenta que stdtambién tiene shared_ptrpor ahora, por lo tanto el uso boosty stdespacios de nombres chocará cuando intenta utilizar una shared_ptr.
Joey
2
Este es un buen ejemplo de por qué muchas casas de software desalientan la importación de espacios de nombres completos de esta manera. No está de más especificar siempre el espacio de nombres, y si son demasiado largos, crear un alias o solo clases específicas importantes del espacio de nombres.
arroz
5

También puede contener "usando el espacio de nombres ..." dentro de una función, por ejemplo:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}
Sombra2531
fuente
3

En términos generales, creo un espacio de nombres para un cuerpo de código si creo que posiblemente haya conflictos de nombre de función o tipo con otras bibliotecas. También ayuda al código de marca, ala boost :: .

Adam Hollidge
fuente
3

Prefiero usar un espacio de nombres de nivel superior para la aplicación y espacios de nombres secundarios para los componentes.

La forma en que puede usar las clases de otros espacios de nombres es sorprendentemente muy similar a la de Java. Puede usar "use NAMESPACE", que es similar a una instrucción "import PACKAGE", por ejemplo, use std. O puede especificar el paquete como prefijo de la clase separada con "::", por ejemplo, std :: string. Esto es similar a "java.lang.String" en Java.

dmeister
fuente
3

Tenga en cuenta que un espacio de nombres en C ++ realmente es solo un espacio de nombres. No proporcionan nada de la encapsulación que hacen los paquetes en Java, por lo que probablemente no los usará tanto.

Kristopher Johnson
fuente
2

He usado espacios de nombres de C ++ de la misma manera que lo hago en C #, Perl, etc. Es solo una separación semántica de símbolos entre material de biblioteca estándar, material de terceros y mi propio código. Colocaría mi propia aplicación en un espacio de nombres, luego un componente de biblioteca reutilizable en otro espacio de nombres para la separación.

Spoulson
fuente
2

Otra diferencia entre java y C ++ es que en C ++, la jerarquía del espacio de nombres no necesita maquinar el diseño del sistema de archivos. Así que tiendo a colocar una biblioteca reutilizable completa en un solo espacio de nombres y subsistemas dentro de la biblioteca en subdirectorios:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

Solo pondría los subsistemas en espacios de nombres anidados si existiera la posibilidad de un conflicto de nombres.

KeithB
fuente