C ++: Namespaces - como usar em arquivos de cabeçalho e fonte corretamente?

88

Considere um par de dois arquivos de origem: um arquivo de declaração de interface ( *.hou *.hpp) e seu arquivo de implementação ( *.cpp).

Deixe o *.harquivo ser assim:

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

Eu vi duas práticas diferentes para usar namespaces em arquivos de origem:

*.cpp mostrando a prática nº 1:

#include "MyClass.h"
using namespace MyNamespace;

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

*.cpp mostrando a prática 2:

#include "MyClass.h"
namespace MyNamespace {

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

}

Minha pergunta: há alguma diferença entre essas duas práticas e uma é considerada melhor do que a outra?

Nickolay
fonte
30
Também há a opção 3: apenas nós o nome completo, por exemplo int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Possível duplicata: stackoverflow.com/questions/7789163/…
David
@Dave não duplicado. Essas questões se complementam. Recomende adicionar o link fornecido por Dave como "Leia também ..." a esta questão. Minha pergunta ajudará os novatos a escolher o estilo correto.
nickolay
Possível duplicata: stackoverflow.com/questions/8210935/…
Firedragon

Respostas:

62

Do ponto de vista da legibilidade do código, provavelmente é melhor na minha opinião usar o método # 2 por este motivo:

Você pode ter usingvários namespaces de uma vez, e qualquer objeto ou função escrito abaixo dessa linha pode pertencer a qualquer um desses namespaces (exceto conflitos de nomenclatura). Empacotar todo o arquivo em um namespacebloco é mais explícito e permite que você declare novas funções e variáveis ​​que pertencem a esse namespace dentro do arquivo .cpp também

Dan F
fonte
A pergunta que Dave vinculou em seu comentário à sua pergunta também descreve alguns pontos-chave nas diferenças (se houver) entre os dois métodos que você está examinando
Dan F
Pessoal, eu realmente não sei qual resposta selecionar. Eles têm interseção enquanto se complementam.
nickolay
Apenas comentando para reconhecer que alguns IDEs como o CLion só detectarão implementações se você usar a opção / prática # 2.
PedroTanaka
@PedroTanaka ainda é o caso? Eu não percebi esse problema.
John McFarlane
@JMcF Não verifiquei desde a publicação do comentário. Nas primeiras versões do Clion, o problema ocorreu.
PedroTanaka
51

O mais claro é a opção que você não mostrou:

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

Também é muito prolixo; demais para a maioria das pessoas. Como using namespaceé um recurso para conflitos de nomes, pelo menos na minha experiência, e deve ser evitado, exceto em locais e escopos muito limitados, geralmente uso o seu # 2.

James Kanze
fonte
3
Obrigado muito claro. Juntos, criamos uma boa página de perguntas frequentes para usuários de namespaces. :)
nickolay
2
Pessoal, eu realmente não sei qual resposta selecionar. Eles têm interseção enquanto se complementam.
nickolay
10

Existem diferenças entre essas duas práticas

Sim. # 1 e # 2 são exemplos de uma diretiva de uso e uma definição de namespace, respectivamente. Eles são efetivamente os mesmos neste caso, mas têm outras consequências. Por exemplo, se você introduzir um novo identificador ao lado MyClass::foo, ele terá um escopo diferente:

# 1:

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

# 2:

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

um é considerado melhor que o outro?

# 1 Prós: um pouco mais conciso; mais difícil introduzir algo acidentalmente MyNamespacesem querer. Contras: pode obter identificadores existentes de forma não intencional.

# 2 Prós: mais claro que definições de identificadores existentes e declarações de novos identificadores pertencem MyNamespace. Contras: é mais fácil introduzir identificadores involuntariamente MyNamespace.

Uma crítica tanto ao nº 1 quanto ao nº 2 é que eles estão se referindo a um namespace inteiro quando você provavelmente só se preocupa com a definição dos membros de MyNamespace::MyClass. Isso é pesado e comunica mal a intenção.

Uma possível alternativa ao nº 1 é uma declaração de uso que inclui apenas o identificador no qual você está interessado:

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

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

Também gostaria de acrescentar que, se por algum motivo você decidir implementar uma especialização de modelo em um arquivo cpp e confiar apenas em using namespacevocê, terá o seguinte 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(){}

Caso contrário, se você aplicar o método # 2, estará tudo bem.

Jordânia
fonte
0

Eu gostaria de adicionar mais uma maneira, usando a declaração de uso :

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

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

Desta forma evita que você digite o nome do namespace muito tempo se a classe tiver muitas funções

Joanna
fonte