22.09
2020
Muita gente usa namespace e não sabe exatamente nem por quê, nem como funciona. Namespaces é uma forma de isolar identificadores com contexto global, evitando o conflito de nomes. Assim podemos criar novos identificadores sem nenhuma preocupação se eles estão em uso por parte de outro trecho do código, ou por outra biblioteca.
Declaração
A declaração de um namespace é bem simples., basta utilizar a palavra chave namespace, seguida do nome do namespace e chaves. Tudo o que for declarado dentro das chaves vai estar contido dentro do namespace, e para acessá-los de fora é preciso utilizar o operador ::, ou através do using namespace.
Um bom exemplo seria uma biblioteca de containers equivalente à STL, porém certificada para um fim específico:
namespace cert {
template < class T, class Alloc = allocator<T> >
class list {
/* ... */
}
}
Dessa forma a classe list declarada dentro do namespace cert é diferente da declarada dentro do namespace std, e ambas podem conviver pacificamente, sem conflitos, dentro da mesma base de código.
É possível também criar um namespace anônimo. Neste caso o efeito é o mesmo de declarar uma variável global como estática: somente estará acessível dentro do próprio arquivo. Por exemplo, o código abaixo vai dar erro de linkagem porque o acesso à variável x em file2.cpp não será permitido.
file1.cpp
namespace {
int x;
}
void f1() { x = 1; }
file2.cpp
extern int x; // compila ok
void f2() {
x = 2; // falha na linkagem
}
É possível também definir namespaces aninhados:
namespace cert {
int x;
namespace do178 {
int y;
}
}
int main() {
int x = cert::x;
int y = cert::do178::y;
}
Utilização
No exemplo da classe list contida no namespace cert temos duas opções para utilizá-la. A primeira é acessar o nome da classe utilizando o nome do namespace:
#include <cert/list>
int main() {
cert::list<int> l;
/* ... */
}
A outra forma é utilizando using:
#include <cert/list>
using namespace cert;
int main() {
list<int> l;
/* ... */
}
Que é a forma mais popular para utilizar o namespace std, padrão do STL. A maioria dos desenvolvedores utiliza using namespace std; para acessar as classes do STL, mas não sabia exatamente o porquê. Como no exemplo:
#include <iostream>
using namespace std;
int main() {
cout << "Testing..." << endl;
}
É ainda possível utilizar somente uma entidade membro do namespace, reduzindo ou eliminando a chance de conflito. Por exemplo:
#include <iostream>
#include <cert/list>
using std::cout;
using cert::list;
int main() {
cout << "using " << std::endl;
list<int> l;
/* ... */
}
Note que se forem utilizados os dois namespaces em questão, std e cert, haverá conflito nos nomes das classes, gerando um erro.
Quando Utilizar Cada Opção?
Sempre que o assunto for estilo, a decisão final sempre fica a cargo da equipe. Cada equipe define a melhor forma de declarar e principalmente utilizar namespaces. A grande vantagem de utilizar using é reduzir a quantidade de código a ser digitado. Outra vantagem é poder trocar entre namespaces equivalentes, através da alteração de uma única linha em cada arquivo.
Por outro lado utilizar o identificador completo, com todos os namespaces, deixa explicito sobre qual namespace a declaração se trata. Portanto é mais indicado quando é necessário ser mais didático, pois indica com exatidão em quais namespaces as delcarações pertencem.
No Lab C++ não é utilizado using, pelo fato que é necessário ser didático e deixar bem claro os namespaces, portanto não estranhe quando ver o seguinte código:
std::cout <<
std::setfill('.') << std::setw(20) << std::left <<
it->first <<
" $ " <<
std::setfill(' ') << std::setw(8) << std::right <<
std::fixed << std::setprecision(2) <<
it->second <<
std::endl;
Apesar de ficar um pouco maior que o necessário ninguém fica na dúvida do namespace.