que - Funciones ''Helper'' en C++
namespace que es (7)
Al refacturar un código antiguo, eliminé una serie de métodos públicos que en realidad deberían haber sido estáticos, ya que a) no operan en ningún dato de miembro ni llaman a ninguna otra función de miembro, yb) porque pueden resultar útiles en cualquier otro lugar.
Esto me llevó a pensar en la mejor forma de agrupar funciones de ''ayudante''. El modo Java / C # sería usar una clase de funciones estáticas con un constructor privado, por ejemplo:
class Helper
{
private:
Helper() { }
public:
static int HelperFunc1();
static int HelperFunc2();
};
Sin embargo, al ser C ++ también puedes usar un espacio de nombres:
namespace Helper
{
int HelperFunc1();
int HelperFunc2();
}
En la mayoría de los casos, creo que preferiría el enfoque del espacio de nombres, pero quería saber cuáles son los pros y los contras de cada enfoque. Si se utiliza el enfoque de clase, por ejemplo, ¿habría algún gasto indirecto?
La principal ventaja de usar un espacio de nombres es que puedes volver a abrirlo y agregar más cosas más tarde, no puedes hacer eso con una clase. Esto hace que este enfoque sea mejor para los asistentes poco acoplados (por ejemplo, podría tener un espacio de nombres de Helpers para toda la biblioteca, al igual que todo STL está en :: std)
La principal ventaja de una clase es que puedes anidarla dentro de la clase utilizándola, no puedes anidar un espacio de nombres en una clase. Esto hace que este enfoque sea mejor para los ayudantes estrechamente acoplados.
No tendrá ningún gasto extra que los tenga en una clase frente a un espacio de nombres.
La sobrecarga no es un problema, aunque los espacios de nombres tienen algunas ventajas
- Puede volver a abrir un espacio de nombres en otro encabezado, agrupando las cosas de manera más lógica, manteniendo bajas las dependencias de compilación
Puede utilizar el alias de espacio de nombre para su ventaja (depuración / liberación, ayudantes específicos de la plataforma, ...)
por ejemplo, he hecho cosas como
namespace LittleEndianHelper { void Function(); } namespace BigEndianHelper { void Function(); } #if powerpc namespace Helper = BigEndianHelper; #elif intel namespace Helper = LittleEndianHelper; #endif
Los espacios de nombres ofrecen la ventaja adicional de la búsqueda de Koenig. El uso de clases auxiliares puede hacer que su código sea más detallado: por lo general, debe incluir el nombre de la clase auxiliar en la llamada.
Otro beneficio para los espacios de nombres es la legibilidad más adelante. Con las clases, debe incluir palabras como "Ayuda" para recordarle más tarde que la clase en particular no se usa para crear objetos
En la práctica, no hay gastos generales en ninguno de los dos. Después de la compilación, solo difiere el nombre "mangling".
Tiendo a usar espacios de nombres anónimos cuando creo funciones auxiliares. Debido a que (generalmente) solo deben ser vistos por el módulo que se preocupa por ellos, es una buena manera de controlar las dependencias.
Para agregar a la excelente respuesta de Pieter, otra ventaja de los espacios de nombres es que puedes reenviar las cosas que pones en un espacio de nombres en otro lugar, especialmente las estructuras ...
//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);
};
Y con espacios de nombres ...
//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);
};
Forward declara hacer una gran diferencia en sus tiempos de compilación después de pequeños cambios de encabezado una vez que termina con un proyecto que abarca cientos de archivos fuente.
Editar desde paercebal
La respuesta fue demasiado buena para dejarla morir debido a un error de enumeración (ver comentarios). Reemplacé las enumeraciones (que se pueden declarar hacia delante solo en C ++ 0x, no en C ++ hoy en día) por structs.
Un caso en el que se podría usar una class
(o struct
) sobre el namespace
es cuando se necesita un tipo, por ejemplo:
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;
}
Parte copiada / recortada / revisada de mi respuesta de ¿Cómo se usan correctamente los espacios de nombres en C ++? .
Usando "usar"
Puede usar "usar" para evitar repetir el "prefijo" de su función auxiliar. por ejemplo:
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
}
Composición del espacio de nombres
Los espacios de nombres son más que paquetes. Otro ejemplo se puede encontrar en "The C ++ Programming Language" de Bjarne Stroustrup.
En la "Edición especial", en 8.2.8 Composición del espacio de nombres , describe cómo puede fusionar dos espacios de nombres AAA y BBB en otro llamado CCC. Así 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 personalizado. Todavía tengo que encontrar un uso práctico de esto, pero en teoría, es genial.