c++ - usan - se puede usar hashtags en linkedin
función miembro privada estática o función libre en el espacio de nombres anónimo? (7)
Recientemente hice un cambio estilístico y quería ver cómo se sentían otros programadores de c ++ al respecto y si existían inconvenientes.
Esencialmente, cuando necesitaba una función de utilidad que no necesita acceso a un miembro de clase determinado, lo que solía hacer era algo como esto:
archivo.h
class A {
public:
// public interface
private:
static int some_function(int);
};
archivo.cpp
int A::some_function(int) {
// ...
}
Pero más recientemente, he preferido hacer algo como esto:
archivo.cpp
namespace {
int some_function(int) {
}
}
// the rest of file.cpp
Aquí está mi proceso de pensamiento:
- es un archivo menos la edición
- el simple hecho de tener la función enlistada en el archivo de encabezado puede sugerir detalles de la implementación que no necesitan ser expuestos (incluso si no están disponibles públicamente).
- si el prototipo de la función necesita cambiar, solo se necesita recompilar un archivo.
El último es el más convincente para mí. Entonces mi pregunta es: ¿hay algún inconveniente en esto?
Son funcionalmente equivalentes para la mayoría de los propósitos que se me ocurren. Me parece que una función private static
casi siempre se puede convertir a una función gratuita en un namespace
anónimo.
EDITAR : Una cosa que viene a la mente es que una función private static
tendría acceso a miembros private
si se le da un puntero a un objeto para operar, pero si ese es el caso, ¿por qué no convertirlo en un miembro no static
?
¿Qué piensan ustedes?
Empecé a hacer esto hace varios años y nunca encontré ningún inconveniente real. Cualquier cosa que use el objeto, pero no cambia el estado del objeto y no necesita ser utilizado en ningún otro lugar, me gusta poner un espacio de nombre anónimo en el archivo de implementación.
La razón principal de la función estática en oposición a la función global es que accede a los miembros privados de la clase. Si esto no es cierto, use una función gratuita. Este análisis no es una respuesta completa hasta que tengamos las 3 formas alternativas de hacerlo:
función libre:
void f() { /* cannot access private members of any class */ }
función estática:
class A {
public:
static void g() { /* can access private members of class A */ }
};
funciones de amigo:
class A {
public:
friend void h();
};
class B {
public:
friend void h();
};
void h() { /* can access private members of both class A and class B */ }
El problema real de cómo c ++ hace esto es que la llamada a la función se ve diferente:
int main() {
f();
A::g();
h();
}
Las variables estáticas no constantes privadas son inútiles para cualquier código que no esté en .cpp, por lo que siempre uso un espacio de nombres anónimo como ese.
Las variables constantes estáticas pueden documentar restricciones y expectativas que son útiles para el código del cliente, por lo que van en el encabezado.
Las funciones estáticas pueden necesitar acceso a miembros de datos privados, por lo que van en el encabezado si es necesario .
No puedo ver ningún inconveniente al uso de un espacio de nombre anónimo. Si puede codificar la función de manera útil sin acceso a los miembros de la clase, entonces debería hacerlo, ya que la desacopla de la clase misma (como la forma en que los algoritmos estándar funcionan en pares de iteradores).
Personalmente, las únicas funciones estáticas que uso son funciones de fábrica, como esta
class Angle {
public:
static Angle FromDegree (float v);
static Angle FromRadians (float v);
...
private:
Angle (float degree);
};
Todo lo que no necesita acceso privado se convierte en una función gratuita. Todo lo que no sea parte de la interfaz pública es, si es posible, no colocado en un lugar público (conforme a su nuevo enfoque).
El artículo de Scott Meyer "Cómo las funciones no miembro mejoran la encapsulación" describe aproximadamente el mismo enfoque:
Minimalidad y encapsulación
En C ++ efectivo, defendí las interfaces de clase que son completas y mínimas. Tales interfaces les permiten a los clientes de la clase hacer cualquier cosa que razonablemente quieran hacer, pero las clases no contienen más funciones de miembros de las que son absolutamente necesarias. Agregando funciones más allá del mínimo necesario para que los clientes puedan realizar su trabajo, escribí, disminuye la capacidad de comprensión y la capacidad de mantenimiento de la clase, además de que aumenta los tiempos de compilación para los clientes. Jack Reeves ha escrito que la adición de funciones miembro más allá de las realmente requeridas viola el principio abierto / cerrado, produce interfaces de clase grasa y, en última instancia, conduce a la pudrición del software. Eso es un buen número de argumentos para minimizar el número de funciones miembro en una clase, pero ahora tenemos una razón adicional: no hacerlo reduce la encapsulación de una clase.
Por supuesto, una interfaz de clase mínima no es necesariamente la mejor interfaz. Comentaba en Effective C ++ que agregar funciones más allá de las realmente necesarias puede ser justificable si mejora significativamente el rendimiento de la clase, hace que la clase sea más fácil de usar o evita posibles errores de los clientes. Basado en su trabajo con varias clases similares a cuerdas, Jack Reeves ha observado que algunas funciones simplemente no "se sienten bien" cuando se las hace no miembros, incluso si pudieran ser no amigos que no son miembros. La "mejor" interfaz para una clase solo se puede encontrar al equilibrar muchas preocupaciones en competencia, de las cuales el grado de encapsulación es solo uno.
Aún así, la lección de este artículo debe ser clara. A pesar de la sabiduría convencional, el uso de funciones ajenas al no amigo mejora la encapsulación de una clase, y la preferencia por tales funciones sobre las funciones miembro facilita el diseño y el desarrollo de clases con interfaces completas y mínimas (o casi mínimas). Los argumentos acerca de la naturalidad de la sintaxis de llamada resultante generalmente son infundados, y la adopción de una predilección por funciones ajenas al no amigo conduce a estrategias de empaquetamiento para la interfaz de una clase que minimizan las dependencias de compilación del cliente y maximizan el número de funciones convenientes disponibles para ellos.
Es hora de abandonar las ideas tradicionales, pero inexactas, de lo que significa estar orientado a objetos. ¿Eres un verdadero creyente en la encapsulación? Si es así, sé que abrazarás las funciones que no son amigos no miembros con el fervor que merecen.
Si está usando una compilación de la unidad (es decir, # incluyendo todos los archivos .cpp del proyecto en una unidad de compilación para acelerar los tiempos de compilación), se arriesgará a colisiones de nombres con otras funciones en espacios de nombres anónimos.
Ese es el único inconveniente que he encontrado.
Si la función solo se usa en un archivo de origen, tiene mucho sentido definirlo allí. Si nadie más lo está usando, no pertenece al encabezado.
Como dices, reduce las dependencias y puede guardar algunas compilaciones.