Pergunta

Ao refatorar alguns códigos antigos, eliminei vários métodos públicos que deveriam ser estáticos, pois a) não operam em nenhum dado de membro nem chamam nenhuma outra função de membro eb) porque podem ser úteis em outro lugar.

Isso me levou a pensar sobre a melhor maneira de agrupar funções 'auxiliares'.A maneira Java/C# seria usar uma classe de funções estáticas com um construtor privado, por exemplo:

class Helper  
{  
private:  
  Helper() { }
public:  
  static int HelperFunc1();  
  static int HelperFunc2();  
};

No entanto, sendo C++ você também pode usar um namespace:

namespace Helper  
{  
  int HelperFunc1();  
  int HelperFunc2();  
}

Na maioria dos casos, acho que preferiria a abordagem de namespace, mas queria saber quais são os prós e os contras de cada abordagem.Se usada a abordagem de classe, por exemplo, haveria alguma sobrecarga?

Foi útil?

Solução

A sobrecarga não é um problema, mas os namespaces têm algumas vantagens

  • Você pode reabrir um namespace em outro cabeçalho, agrupando as coisas de forma mais lógica enquanto mantendo as dependências de compilação baixas
  • Você pode usar o aliasing de namespace a seu favor (depuração/lançamento, auxiliares específicos da plataforma, ....)

    por exemplo.Eu fiz coisas como

    namespace LittleEndianHelper {
       void Function();
    }
    namespace BigEndianHelper {
       void Function();
    }
    
    #if powerpc
       namespace Helper = BigEndianHelper;
    #elif intel
       namespace Helper = LittleEndianHelper;
    #endif
    

Outras dicas

Um caso em que se pode usar class (ou struct) sobre namespace é quando se precisa de um tipo, por exemplo:

struct C {
  static int f() { return 33; }
};

namespace N {
  int f() { return 9; }
}

template<typename T>
int foo() {
  return T::f();
}

int main() {
  int ret = foo<C>();
//ret += foo<N>(); // compile error: N is a namespace
  return ret;
}

Para complementar a excelente resposta de Pieter, outra vantagem dos namespaces é que você pode encaminhar e declarar coisas que você colocou em um namespace em outro lugar, especialmente estruturas...

//Header a.h
// Lots of big header files, spreading throughout your code
class foo
{
  struct bar {/* ... */);
};

//header b.h
#include a.h // Required, no way around it, pulls in big headers
class b
{
  //...
  DoSomething(foo::bar);
};

E com namespaces...

//Header a.h
// Big header files
namespace foo
{
  struct bar {/* ... */);
}

//header b.h
// Avoid include, instead forward declare 
//  (can put forward declares in a _fwd.h file)
namespace foo
{
  struct bar;
}

class b
{
  //...
  // note that foo:bar must be passed by reference or pointer
  void DoSomething(const foo::bar & o);
};

As declarações de encaminhamento fazem uma grande diferença nos tempos de compilação após pequenas alterações de cabeçalho, uma vez que você termina com um projeto abrangendo centenas de arquivos de origem.

Editar de paercebal

A resposta foi boa demais para deixá-la morrer devido a um erro de enum (veja os comentários).Substituí enums (que podem ser declarados posteriormente apenas em C++ 0x, não no C++ atual) por estruturas.

A principal vantagem de usar um namespace é que você pode reabri-lo e adicionar mais coisas posteriormente, mas não pode fazer isso com uma classe.Isso torna essa abordagem melhor para auxiliares fracamente acoplados (por exemplo, você pode ter um namespace Helpers para toda a sua biblioteca, assim como todo o STL está em ::std)

A principal vantagem de uma classe é que você pode aninhá-la dentro da classe que a utiliza, mas não pode aninhar um namespace em uma classe.Isso torna essa abordagem melhor para ajudantes fortemente acoplados.

Você não terá nenhuma sobrecarga extra tendo-os em uma classe versus um namespace.

Namespaces oferecem a vantagem adicional da pesquisa Koenig.O uso de classes auxiliares pode tornar seu código mais detalhado - geralmente você precisa incluir o nome da classe auxiliar na chamada.

Outro benefício dos namespaces é a legibilidade posterior.Com classes, você precisa incluir palavras como "Helper" para lembrá-lo mais tarde de que a classe específica não é usada para criar objetos

Na prática, também não há sobrecarga.Após a compilação, apenas o nome mangling usado difere.

Copiado/cortado/retrabalhado parte da minha resposta de Como você usa namespaces corretamente em C++?.

Usando "usando"

Você pode usar "using" para evitar repetir o "prefixo" da sua função auxiliar.por exemplo:

struct AAA
{
   void makeSomething() ;
} ;

namespace BBB
{
   void makeSomethingElse() ;
}

void willCompile()
{
   AAA::makeSomething() ;
   BBB::makeSomethingElse() ;
}

void willCompileAgain()
{
   using BBB ;

   makeSomethingElse() ; // This will call BBB::makeSomethingElse()
}

void WONT_COMPILE()
{
   using AAA ; // ERROR : Won't compile

   makeSomething() ; // ERROR : Won't compile
}

Composição do namespace

Namespaces são mais do que pacotes.Outro exemplo pode ser encontrado em "The C++ Programming Language" de Bjarne Stroustrup.

Na "Edição Especial", em 8.2.8 Composição do Namespace, ele descreve como você pode mesclar dois namespaces AAA e BBB em outro chamado CCC.Assim, CCC se torna um apelido para AAA e BBB:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

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

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

Você pode até importar símbolos selecionados de diferentes namespaces para criar sua própria interface de namespace personalizada.Ainda não encontrei um uso prático para isso, mas, em teoria, é legal.

Costumo usar namespaces anônimos ao criar funções auxiliares.Como eles (geralmente) só devem ser vistos pelo módulo que se preocupa com eles, é uma boa maneira de controlar dependências.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top