Espacio de nombres + funciones versus métodos estáticos en una clase

Digamos que tengo, o voy a escribir, un conjunto de funciones relacionadas. Digamos que están relacionados con las matemáticas. Organizacionalmente, debería yo:

  1. Escriba estas funciones y colóquelas en mi espacio de nombres MyMath y MyMath referencia a ellas a través de MyMath::XYZ()
  2. Cree una clase llamada MyMath y establezca estos métodos como estáticos y haga referencia a MyMath::XYZ() manera similar

¿Por qué elegiría uno sobre el otro como medio para organizar mi software?

De forma predeterminada, usa funciones con espacios de nombres.

Las clases son para construir objetos, no para reemplazar espacios de nombres.

En código orientado a objetos

Scott Meyers escribió un ítem completo para su libro Efectivo de C ++ sobre este tema, “Prefiere las funciones de no miembro no miembro a las funciones de miembro”. Encontré una referencia en línea a este principio en un artículo de Herb Sutter: http://www.gotw.ca/gotw/084.htm

Lo importante es saber que: en las funciones de C ++ en el mismo espacio de nombres, una clase pertenece a la interfaz de esa clase (porque ADL buscará esas funciones cuando resuelva llamadas a funciones).

Las funciones espaciadas por nombre, a menos que se declare “amigo”, no tienen acceso a las partes internas de la clase, mientras que las funciones estáticas sí lo tienen.

Esto significa, por ejemplo, que al mantener su clase, si necesita cambiar las partes internas de su clase, deberá buscar efectos secundarios en todos sus métodos, incluidos los estáticos.

Extensión I

Agregar código a una interfaz de clase.

En C #, puede agregar métodos a una clase incluso si no tiene acceso a ella. Pero en C ++, esto es imposible.

Pero, aún en C ++, puede agregar una función de espacio de nombres, incluso a una clase que alguien escribió para usted.

Ver desde el otro lado, esto es importante al diseñar su código, porque al poner sus funciones en un espacio de nombres, autorizará a los usuarios a boost / completar la interfaz de la clase.

Extensión II

Un efecto secundario del punto anterior, es imposible declarar métodos estáticos en múltiples encabezados. Todos los métodos deben declararse en la misma clase.

Para los espacios de nombres, las funciones del mismo espacio de nombres se pueden declarar en múltiples encabezados (la función de intercambio casi estándar es el mejor ejemplo de eso).

Extensión III

El conocimiento básico de un espacio de nombres es que en algún código, puede evitar mencionarlo, si usa la palabra clave “using”:

 #include  #include  // Etc. { using namespace std ; // Now, everything from std is accessible without qualification string s ; // Ok vector v ; // Ok } string ss ; // COMPILATION ERROR vector vv ; // COMPILATION ERROR 

Y puedes incluso limitar la “contaminación” a una clase:

 #include  #include  { using std::string ; string s ; // Ok vector v ; // COMPILATION ERROR } string ss ; // COMPILATION ERROR vector vv ; // COMPILATION ERROR 

Este “patrón” es obligatorio para el uso adecuado de la expresión de intercambio casi estándar.

Y esto es imposible de hacer con métodos estáticos en las clases.

Entonces, los espacios de nombres C ++ tienen su propia semántica.

Pero va más allá, ya que puede combinar espacios de nombres de forma similar a la herencia.

Por ejemplo, si tiene un espacio de nombres A con una función AAA, un espacio de nombres B con una función BBB, puede declarar un espacio de nombres C y traer AAA y BBB en este espacio de nombres con la palabra clave using.

Conclusión

Los espacios de nombres son para espacios de nombres. Las clases son para clases.

C ++ fue diseñado para que cada concepto sea diferente y se use de manera diferente, en diferentes casos, como solución a diferentes problemas.

No use clases cuando necesite espacios de nombres.

Y en tu caso, necesitas espacios de nombres.

Hay muchas personas que no estarían de acuerdo conmigo, pero así es como lo veo:

Una clase es esencialmente una definición de cierto tipo de objeto. Los métodos estáticos deberían definir operaciones que están íntimamente ligadas a esa definición de objeto.

Si va a tener un grupo de funciones relacionadas no asociadas con un objeto subyacente o una definición de un tipo de objeto , entonces yo diría que vayan solo con un espacio de nombres. Solo para mí, conceptualmente, esto es mucho más sensato.

Por ejemplo, en su caso, pregúntese, “¿Qué es un MyMath?” Si MyMath no define un tipo de objeto, entonces diría: no lo convierta en una clase.

Pero como dije, sé que hay mucha gente que (incluso con vehemencia) no estaría de acuerdo conmigo en esto (en particular, los desarrolladores de Java y C #).

  • Si necesita datos estáticos, use métodos estáticos.
  • Si son funciones de plantilla y le gustaría poder especificar un conjunto de parámetros de plantilla para todas las funciones juntas, utilice métodos estáticos en una clase de plantilla.

De lo contrario, use funciones con espacios de nombres.


En respuesta a los comentarios: sí, los métodos estáticos y los datos estáticos tienden a ser sobreutilizados. Es por eso que ofrecí solo dos escenarios relacionados donde creo que pueden ser útiles. En el ejemplo específico de OP (un conjunto de rutinas matemáticas), si quisiera la capacidad de especificar parámetros, por ejemplo, un tipo de datos básicos y precisión de salida, que se aplicaría a todas las rutinas, podría hacer algo como:

 template class MyMath { // routines operate on datatype T, preserving at least decimalPlaces precision }; // math routines for manufacturing calculations typedef MyMath CAMMath; // math routines for on-screen displays typedef MyMath PreviewMath; 

Si no necesita eso, entonces use un espacio de nombres.

Debe usar un espacio de nombres, porque un espacio de nombres tiene muchas ventajas sobre una clase:

  • No tiene que definir todo en el mismo encabezado
  • No necesita exponer toda su implementación en el encabezado
  • No puedes using un miembro de la clase; puedes using un miembro de espacio de nombres
  • No se puede using class , aunque using namespace no siempre es una buena idea
  • Usar una clase implica que hay algún objeto que crear cuando realmente no hay ninguno

Los miembros estáticos son, en mi opinión, muy muy abusados. No son una necesidad real en la mayoría de los casos. Las funciones de miembros estáticos probablemente sean mejores como funciones de scope de archivo, y los miembros de datos estáticos son solo objetos globales con una reputación mejor e inmerecida.

Preferiría los espacios de nombres, de esa manera usted puede tener datos privados en un espacio de nombre anónimo en el archivo de implementación (para que no tenga que aparecer en el encabezado en lugar de miembros private ). Otro beneficio es que al using su espacio de nombres, los clientes de los métodos pueden optar por no especificar MyMath::

Una razón más para usar class – Option para hacer uso de los especificadores de acceso. Entonces, posiblemente pueda romper su método público estático en métodos privados más pequeños. El método público puede llamar a múltiples métodos privados.

Tanto el espacio de nombres como el método de clase tienen sus usos. El espacio de nombres tiene la capacidad de ser distribuido a través de archivos, sin embargo, eso es una debilidad si necesita hacer cumplir todo el código relacionado para ir en un archivo. Como se mencionó anteriormente, la clase también le permite crear miembros estáticos privados en la clase. Puede tenerlo en el espacio de nombres anónimo del archivo de implementación, sin embargo, sigue siendo un ámbito más amplio que tenerlos dentro de la clase.