Sí. En los viejos tiempos, simplemente lo llamamos una "función". Ustedes niños hoy con sus "espacios de nombres" locos. ¡Oye, sal de mi césped! <sacude el puño>
Vagabundo
77
@Vagrant una función dentro de un espacio de nombres sigue siendo una función. Una función que pertenece a una clase se llama método. Si es un método estático, lo invoca de manera similar como si fuera una función dentro de un espacio de nombres.
48
5 años después y ahora estoy del lado de @Vagrant. ¡Qué pregunta tonta!
Andrew
16
9 años después y ahora tengo mi propio lenguaje de programación que ni siquiera tiene clases: ziglang.org
andrewrk
1
Las clases tipo contenedor IMO (que tienen solo métodos estáticos) son útiles en ciertos casos.
AarCee
Respuestas:
272
Si está buscando una manera de aplicar la palabra clave "estática" a una clase, como puede hacerlo en C #, por ejemplo, no podrá hacerlo sin usar C ++ administrado.
Pero el aspecto de su muestra, solo necesita crear un método público estático en su objeto BitParser. Al igual que:
BitParser.h
classBitParser{public:staticbool getBitAt(int buffer,int bitIndex);// ...lots of great stuffprivate:// Disallow creating an instance of this objectBitParser(){}};
BitParser.cpp
boolBitParser::getBitAt(int buffer,int bitIndex){bool isBitSet =false;// .. determine if bit is setreturn isBitSet;}
Puede usar este código para llamar al método de la misma manera que su código de ejemplo.
OJ, tiene un error de sintaxis . La palabra clave estática solo debe usarse en la definición de clase, y no en la definición del método.
andrewrk
90
Para aclarar su intención en este enfoque, también puede utilizar un constructor privado. private: BitParser() {}Esto evitará que cualquiera cree instancias.
Danvil
77
La seguridad de subprocesos de @MoatazElmasry es un problema cuando compartes estado. En la implementación anterior no hay un estado compartido, por lo que no puede haber ningún problema con la seguridad de subprocesos ... a menos que seas lo suficientemente estúpido como para usar estadísticas dentro de esas funciones. Entonces, sí, el código anterior es seguro para subprocesos, solo mantenga el estado persistente fuera de sus funciones y estará bien.
DO
@MoatazElmasry Incorrecto. Dos hilos no pueden modificar variables locales no estáticas en una función estática.
DO
12
Si es C ++ 11, diría que es mejor BitParser() = delete;transmitir adecuadamente la intención de eliminar el constructor (no solo ocultarlo como private).
Pero debe recordar que las "clases estáticas" son hacks en el tipo de lenguajes similares a Java (por ejemplo, C #) que no pueden tener funciones que no sean miembros, por lo que tienen que moverlas dentro de las clases como métodos estáticos.
En C ++, lo que realmente quiere es una función no miembro que declarará en un espacio de nombres:
En C ++, el espacio de nombres es más poderoso que las clases para el patrón "Método estático Java", porque:
los métodos estáticos tienen acceso a las clases símbolos privados
Los métodos estáticos privados siguen siendo visibles (si no son accesibles) para todos, lo que infringe algo la encapsulación
los métodos estáticos no se pueden declarar hacia adelante
El usuario de la clase no puede sobrecargar los métodos estáticos sin modificar el encabezado de la biblioteca
no hay nada que se pueda hacer con un método estático que no se pueda hacer mejor que una función no miembro (posiblemente amiga) en el mismo espacio de nombres
los espacios de nombres tienen su propia semántica (se pueden combinar, pueden ser anónimos, etc.)
etc.
Conclusión: No copie / pegue el patrón de Java / C # en C ++. En Java / C #, el patrón es obligatorio. Pero en C ++, es un mal estilo.
Editar 2010-06-10
Hubo un argumento a favor del método estático porque a veces, uno necesita usar una variable miembro privada estática.
No estoy de acuerdo, como se muestra a continuación:
Primero, myGlobal se llama myGlobal porque sigue siendo una variable privada global. Un vistazo a la fuente de CPP aclarará que:
// CPP
std::string Foo::myGlobal ;// You MUST declare it in a CPPvoidFoo::barA(){// I can access Foo::myGlobal}voidFoo::barB(){// I can access Foo::myGlobal, too}void barC(){// I CAN'T access Foo::myGlobal !!!}
A primera vista, el hecho de que la función gratuita barC no pueda acceder a Foo :: myGlobal parece algo bueno desde el punto de vista de la encapsulación ... Es genial porque alguien que mira el HPP no podrá (a menos que recurra al sabotaje) acceder Foo :: myGlobal.
Pero si lo observa detenidamente, encontrará que es un error colosal: no solo su variable privada debe ser declarada en el HPP (y, por lo tanto, visible para todo el mundo, a pesar de ser privada), sino que debe declarar en la misma HPP todas (como en TODAS) funciones que estarán autorizadas para acceder a ella !!!
Por lo tanto, usar un miembro estático privado es como caminar desnudo afuera con la lista de tus amantes tatuada en tu piel: nadie está autorizado a tocar, pero todos pueden mirar. Y la bonificación: todos pueden tener los nombres de aquellos autorizados para jugar con sus privilegios.
private de hecho ... :-D
La solución "Espacios de nombres anónimos"
Los espacios de nombres anónimos tendrán la ventaja de hacer que las cosas privadas sean realmente privadas.
Primero, el encabezado HPP
// HPPnamespaceFoo{void barA();}
Solo para asegurarme de haber comentado: no hay una declaración inútil de barB ni myGlobal. Lo que significa que nadie que lea el encabezado sabe lo que está oculto detrás de la barra A.
Entonces, el CPP:
// CPPnamespaceFoo{namespace{
std::string myGlobal ;voidFoo::barB(){// I can access Foo::myGlobal}}void barA(){// I can access myGlobal, too}}void barC(){// I STILL CAN'T access myGlobal !!!}
Como puede ver, al igual que la llamada declaración de "clase estática", fooA y fooB aún pueden acceder a myGlobal. Pero nadie más puede. ¡Y nadie más fuera de este CPP sabe que existen fooB y myGlobal!
A diferencia de la "clase estática" que camina desnudo con su libreta de direcciones tatuada en su piel, el espacio de nombres "anónimo" está completamente vestido , lo que parece bastante mejor encapsulado AFAIK.
¿Realmente importa?
A menos que los usuarios de su código sean saboteadores (les dejaré, como ejercicio, encontrar cómo se puede acceder a la parte privada de una clase pública usando un truco sucio de comportamiento indefinido ...), lo que privatees private, incluso si es visible en la privatesección de una clase declarada en un encabezado.
Aún así, si necesita agregar otra "función privada" con acceso al miembro privado, debe declararlo a todo el mundo modificando el encabezado, lo que es una paradoja en lo que a mí respecta: si cambio la implementación de mi código (la parte CPP), entonces la interfaz (la parte HPP) NO debería cambiar. Citando a Leonidas: "¡ Esto es ENCAPSULACIÓN! "
Editar 2014-09-20
¿Cuándo son realmente mejores los métodos estáticos de clases que los espacios de nombres con funciones que no son miembros?
Cuando necesite agrupar funciones y alimentar ese grupo a una plantilla:
namespace alpha
{void foo();void bar();}structBeta{staticvoid foo();staticvoid bar();};template<typename T>structGamma{void foobar(){
T::foo();
T::bar();}};Gamma<alpha> ga ;// compilation errorGamma<Beta> gb ;// ok
gb.foobar();// ok !!!
Porque, si una clase puede ser un parámetro de plantilla, un espacio de nombres no puede.
GCC admite -fno-access-control, que se puede usar en pruebas de unidad de caja blanca para acceder a miembros de clase que de otro modo serían privados. Esa es la única razón por la que puedo pensar para justificar el uso de un miembro de la clase en lugar de un global anónimo / estático en la implementación.
Tom
8
@Tom: Una solución multiplataforma sería agregar el siguiente código #define private publicen los encabezados ... ^ _ ^ ...
paercebal
1
@Tom: de todos modos, en mi humilde opinión, incluso teniendo en cuenta las pruebas unitarias, las desventajas de "mostrar demasiadas cosas" supera a los profesionales. Supongo que una solución alternativa sería poner el código que se probará en una función que tome los parámetros necesarios (y no más) en un utilitiesespacio de nombres. De esta manera, esta función puede ser la unidad probada, y aún no tienen acceso especial a los miembros privados (ya que se dan como parámetros en la llamada de función) ...
paercebal
@paercebal Estoy a punto de saltar a bordo de su barco, pero tengo una reserva final. Si alguien salta en tu namespacevoluntad, ¿no tendrán acceso a tus globalmiembros, aunque estén ocultos? Obviamente tendrían que adivinar, pero a menos que esté ofuscando intencionalmente su código, los nombres de las variables son bastante fáciles de adivinar.
Zak
@Zak: De hecho, podrían hacerlo, pero solo tratando de hacerlo en el archivo CPP donde se declara la variable myGlobal. El punto es más visibilidad que accesibilidad. En la clase estática, la variable myGlobal es privada, pero aún visible. Esto no es tan importante como parece, pero aún así, en una DLL, mostrar un símbolo que debería ser privado para la DLL en un encabezado exportado podría ser incómodo ... En el espacio de nombres, myGlobal solo existe en el archivo CPP (usted incluso puede ir más lejos y hacerlo estático). Esa variable no aparece en los encabezados públicos.
paercebal
63
También puede crear una función libre en un espacio de nombres:
En algunos casos, es posible que desee tener la encapsulación de datos incluso si la clase es principalmente "estática". Los miembros de la clase privada estática te darán esto. Los miembros del espacio de nombres son siempre públicos y no pueden proporcionar la encapsulación de datos.
Torleif
Si la var "miembro" solo se declara y se accede desde el archivo .cpp, es más privada que una var privada declarada en el archivo .h. NO que recomiendo esta técnica.
jmucchiello
3
@Torleif: Estás equivocado. los espacios de nombres son mejores para la encapsulación que los miembros privados estáticos. Vea mi respuesta para una demostración.
paercebal
1
sí, pero en el espacio de nombres debe mantener el orden de las funciones, en contraste con la clase con miembros estáticos, por ejemplo, vacío a () {b ();} b () {} produciría un error en un espacio de nombres pero no en una clase con miembros estáticos
Moataz Elmasry
13
Si está buscando una manera de aplicar la palabra clave "estática" a una clase, como puede hacer en C #, por ejemplo
Las clases estáticas son solo el compilador que te sujeta a la mano y te impide escribir cualquier método / variable de instancia.
Si solo escribe una clase normal sin ningún método / variable de instancia, es lo mismo, y esto es lo que haría en C ++
No me quejo (especialmente a usted), pero algo de compilación de mano para evitar que escriba o corte / pegue la palabra static200 veces sería algo bueno.
3Dave
De acuerdo, pero una clase estática en C # tampoco hace esto. Simplemente no se compila cuando olvida pegar estática allí :-)
Orion Edwards
Sí, lo suficientemente justo. Mis macros se muestran. Honestamente, si declaro que la clase es estática, el compilador solo debería arrojar un error si intento crear una instancia. Las reglas que requieren que me repita son desagradables y deberían ser las primeras contra la pared cuando llegue la revolución.
3Dave
11
En C ++ desea crear una función estática de una clase (no una clase estática).
Entonces debería poder llamar a la función usando BitParser :: getBitAt () sin crear una instancia de un objeto que supongo que es el resultado deseado.
Cambio: en C ++, los especificadores estáticos o externos solo pueden aplicarse a nombres de objetos o funciones. El uso de estos especificadores con declaraciones de tipo es ilegal en C ++. En C, estos especificadores se ignoran cuando se usan en declaraciones de tipo. Ejemplo:
staticstruct S {// valid C, invalid in C++int i;};
Justificación: los especificadores de clase de almacenamiento no tienen ningún significado cuando se asocian con un tipo. En C ++, los miembros de la clase se pueden declarar con el especificador de clase de almacenamiento estático. Permitir que los especificadores de clase de almacenamiento en las declaraciones de tipo pueda hacer que el código sea confuso para los usuarios.
Y al igual que struct,class también es una declaración de tipo.
Lo mismo puede deducirse caminando por el árbol de sintaxis en el Anexo A.
Como se ha señalado aquí, una mejor manera de lograr esto en C ++ podría ser usar espacios de nombres. Pero como nadie ha mencionado la finalpalabra clave aquí, estoy publicando lo que sería un equivalente directo de static classC # en C ++ 11 o posterior:
classBitParser final
{public:BitParser()=delete;staticboolGetBitAt(int buffer,int pos);};boolBitParser::GetBitAt(int buffer,int pos){// your code}
Puede 'tener' una clase estática en C ++, como se mencionó anteriormente, una clase estática es aquella que no tiene ningún objeto de ella instanciada. En C ++, esto se puede obtener declarando el constructor / destructor como privado. El resultado final es el mismo.
Esto es similar a la forma de C # de hacerlo en C ++
En C # file.cs puede tener var privada dentro de una función pública. Cuando esté en otro archivo, puede usarlo llamando al espacio de nombres con la función como en:
//Init the data (Link error if not done)intTheDataToBeHidden::_var1 =0;intTheDataToBeHidden::_var2 =0;//Implement the namespacenamespaceSharedData{voidSetError(constchar*Message,constchar*Title){//blah using TheDataToBeHidden::_var1, etc}voidDisplayError(void){//blah}}
OtherFile.h
#include"SharedModule.h"
OtherFile.cpp
//Call the functions using the hidden variablesSharedData::SetError("Hello","World");SharedData::DisplayError();
Pero todos pueden ir a TheDataToBeHidden -> No es una solución
Guy L
3
A diferencia de otro lenguaje de programación administrado, "clase estática" NO tiene significado en C ++. Puede hacer uso de la función miembro estática.
Un caso en el que los espacios de nombres pueden no ser tan útiles para lograr "clases estáticas" es cuando se usan estas clases para lograr la composición sobre la herencia. Los espacios de nombres no pueden ser amigos de clases y, por lo tanto, no pueden acceder a miembros privados de una clase.
Una (de las muchas) alternativas, pero la más elegante (en mi opinión) elegante (en comparación con el uso de espacios de nombres y constructores privados para emular el comportamiento estático), la forma de lograr el comportamiento de "clase que no se puede instanciar" en C ++ sería declarar una función virtual pura ficticia con el privatemodificador de acceso.
Si está utilizando C ++ 11, podría hacer un esfuerzo adicional para asegurarse de que la clase no se herede (para emular puramente el comportamiento de una clase estática) utilizando el finalespecificador en la declaración de clase para restringir la herencia de las otras clases. .
// C++11 ONLYclassFoo final {public:staticint someMethod(int someArg);private:virtualvoid __dummy()=0;};
Por tonto e ilógico que pueda parecer, C ++ 11 permite la declaración de una "función virtual pura que no se puede anular", que puede usar junto con la declaración de la clase finalpara implementar pura y completamente el comportamiento estático, ya que esto da como resultado el resultado que la clase no sea heredable y que la función ficticia no se anule de ninguna manera.
// C++11 ONLYclassFoo final {public:staticint someMethod(int someArg);private:// Other private declarationsvirtualvoid __dummy()=0 final;};// Foo now exhibits all the properties of a static class
Respuestas:
Si está buscando una manera de aplicar la palabra clave "estática" a una clase, como puede hacerlo en C #, por ejemplo, no podrá hacerlo sin usar C ++ administrado.
Pero el aspecto de su muestra, solo necesita crear un método público estático en su objeto BitParser. Al igual que:
BitParser.h
BitParser.cpp
Puede usar este código para llamar al método de la misma manera que su código de ejemplo.
¡Espero que ayude! Salud.
fuente
private: BitParser() {}
Esto evitará que cualquiera cree instancias.BitParser() = delete;
transmitir adecuadamente la intención de eliminar el constructor (no solo ocultarlo comoprivate
).Considere la solución de Matt Price .
Lo que desea es, expresado en semántica en C ++, colocar su función (porque es una función) en un espacio de nombres.
Editar 2011-11-11
No hay "clase estática" en C ++. El concepto más cercano sería una clase con solo métodos estáticos. Por ejemplo:
Pero debe recordar que las "clases estáticas" son hacks en el tipo de lenguajes similares a Java (por ejemplo, C #) que no pueden tener funciones que no sean miembros, por lo que tienen que moverlas dentro de las clases como métodos estáticos.
En C ++, lo que realmente quiere es una función no miembro que declarará en un espacio de nombres:
¿Porqué es eso?
En C ++, el espacio de nombres es más poderoso que las clases para el patrón "Método estático Java", porque:
Conclusión: No copie / pegue el patrón de Java / C # en C ++. En Java / C #, el patrón es obligatorio. Pero en C ++, es un mal estilo.
Editar 2010-06-10
Hubo un argumento a favor del método estático porque a veces, uno necesita usar una variable miembro privada estática.
No estoy de acuerdo, como se muestra a continuación:
La solución "Miembro privado estático"
Primero, myGlobal se llama myGlobal porque sigue siendo una variable privada global. Un vistazo a la fuente de CPP aclarará que:
A primera vista, el hecho de que la función gratuita barC no pueda acceder a Foo :: myGlobal parece algo bueno desde el punto de vista de la encapsulación ... Es genial porque alguien que mira el HPP no podrá (a menos que recurra al sabotaje) acceder Foo :: myGlobal.
Pero si lo observa detenidamente, encontrará que es un error colosal: no solo su variable privada debe ser declarada en el HPP (y, por lo tanto, visible para todo el mundo, a pesar de ser privada), sino que debe declarar en la misma HPP todas (como en TODAS) funciones que estarán autorizadas para acceder a ella !!!
Por lo tanto, usar un miembro estático privado es como caminar desnudo afuera con la lista de tus amantes tatuada en tu piel: nadie está autorizado a tocar, pero todos pueden mirar. Y la bonificación: todos pueden tener los nombres de aquellos autorizados para jugar con sus privilegios.
private
de hecho ... :-DLa solución "Espacios de nombres anónimos"
Los espacios de nombres anónimos tendrán la ventaja de hacer que las cosas privadas sean realmente privadas.
Primero, el encabezado HPP
Solo para asegurarme de haber comentado: no hay una declaración inútil de barB ni myGlobal. Lo que significa que nadie que lea el encabezado sabe lo que está oculto detrás de la barra A.
Entonces, el CPP:
Como puede ver, al igual que la llamada declaración de "clase estática", fooA y fooB aún pueden acceder a myGlobal. Pero nadie más puede. ¡Y nadie más fuera de este CPP sabe que existen fooB y myGlobal!
A diferencia de la "clase estática" que camina desnudo con su libreta de direcciones tatuada en su piel, el espacio de nombres "anónimo" está completamente vestido , lo que parece bastante mejor encapsulado AFAIK.
¿Realmente importa?
A menos que los usuarios de su código sean saboteadores (les dejaré, como ejercicio, encontrar cómo se puede acceder a la parte privada de una clase pública usando un truco sucio de comportamiento indefinido ...), lo que
private
esprivate
, incluso si es visible en laprivate
sección de una clase declarada en un encabezado.Aún así, si necesita agregar otra "función privada" con acceso al miembro privado, debe declararlo a todo el mundo modificando el encabezado, lo que es una paradoja en lo que a mí respecta: si cambio la implementación de mi código (la parte CPP), entonces la interfaz (la parte HPP) NO debería cambiar. Citando a Leonidas: "¡ Esto es ENCAPSULACIÓN! "
Editar 2014-09-20
¿Cuándo son realmente mejores los métodos estáticos de clases que los espacios de nombres con funciones que no son miembros?
Cuando necesite agrupar funciones y alimentar ese grupo a una plantilla:
Porque, si una clase puede ser un parámetro de plantilla, un espacio de nombres no puede.
fuente
#define private public
en los encabezados ... ^ _ ^ ...utilities
espacio de nombres. De esta manera, esta función puede ser la unidad probada, y aún no tienen acceso especial a los miembros privados (ya que se dan como parámetros en la llamada de función) ...namespace
voluntad, ¿no tendrán acceso a tusglobal
miembros, aunque estén ocultos? Obviamente tendrían que adivinar, pero a menos que esté ofuscando intencionalmente su código, los nombres de las variables son bastante fáciles de adivinar.También puede crear una función libre en un espacio de nombres:
En BitParser.h
En BitParser.cpp
En general, esta sería la forma preferida de escribir el código. Cuando no hay necesidad de un objeto, no use una clase.
fuente
Las clases estáticas son solo el compilador que te sujeta a la mano y te impide escribir cualquier método / variable de instancia.
Si solo escribe una clase normal sin ningún método / variable de instancia, es lo mismo, y esto es lo que haría en C ++
fuente
static
200 veces sería algo bueno.En C ++ desea crear una función estática de una clase (no una clase estática).
Entonces debería poder llamar a la función usando BitParser :: getBitAt () sin crear una instancia de un objeto que supongo que es el resultado deseado.
fuente
¿Puedo escribir algo como
static class
?No , de acuerdo con el borrador estándar C ++ 11 N3337 del Anexo C 7.1.1:
Y al igual que
struct
,class
también es una declaración de tipo.Lo mismo puede deducirse caminando por el árbol de sintaxis en el Anexo A.
Es interesante notar que
static struct
era legal en C, pero no tuvo efecto: ¿por qué y cuándo usar estructuras estáticas en la programación en C?fuente
Como se ha señalado aquí, una mejor manera de lograr esto en C ++ podría ser usar espacios de nombres. Pero como nadie ha mencionado la
final
palabra clave aquí, estoy publicando lo que sería un equivalente directo destatic class
C # en C ++ 11 o posterior:fuente
Puede 'tener' una clase estática en C ++, como se mencionó anteriormente, una clase estática es aquella que no tiene ningún objeto de ella instanciada. En C ++, esto se puede obtener declarando el constructor / destructor como privado. El resultado final es el mismo.
fuente
En Managed C ++, la sintaxis de clase estática es: -
... mejor tarde que nunca...
fuente
Esto es similar a la forma de C # de hacerlo en C ++
En C # file.cs puede tener var privada dentro de una función pública. Cuando esté en otro archivo, puede usarlo llamando al espacio de nombres con la función como en:
Aquí se explica cómo hacer lo mismo en C ++:
SharedModule.h
SharedModule.cpp
OtherFile.h
OtherFile.cpp
fuente
A diferencia de otro lenguaje de programación administrado, "clase estática" NO tiene significado en C ++. Puede hacer uso de la función miembro estática.
fuente
Un caso en el que los espacios de nombres pueden no ser tan útiles para lograr "clases estáticas" es cuando se usan estas clases para lograr la composición sobre la herencia. Los espacios de nombres no pueden ser amigos de clases y, por lo tanto, no pueden acceder a miembros privados de una clase.
fuente
Una (de las muchas) alternativas, pero la más elegante (en mi opinión) elegante (en comparación con el uso de espacios de nombres y constructores privados para emular el comportamiento estático), la forma de lograr el comportamiento de "clase que no se puede instanciar" en C ++ sería declarar una función virtual pura ficticia con el
private
modificador de acceso.Si está utilizando C ++ 11, podría hacer un esfuerzo adicional para asegurarse de que la clase no se herede (para emular puramente el comportamiento de una clase estática) utilizando el
final
especificador en la declaración de clase para restringir la herencia de las otras clases. .Por tonto e ilógico que pueda parecer, C ++ 11 permite la declaración de una "función virtual pura que no se puede anular", que puede usar junto con la declaración de la clase
final
para implementar pura y completamente el comportamiento estático, ya que esto da como resultado el resultado que la clase no sea heredable y que la función ficticia no se anule de ninguna manera.fuente