¿Cómo se crea una clase estática en C ++? Debería poder hacer algo como:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Asumiendo que creé la BitParser
clase. ¿Cómo sería la BitParser
definición de clase?
¿Cómo se crea una clase estática en C ++? Debería poder hacer algo como:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Asumiendo que creé la BitParser
clase. ¿Cómo sería la BitParser
definición de clase?
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
class BitParser
{
public:
static bool getBitAt(int buffer, int bitIndex);
// ...lots of great stuff
private:
// Disallow creating an instance of this object
BitParser() {}
};
BitParser.cpp
bool BitParser::getBitAt(int buffer, int bitIndex)
{
bool isBitSet = false;
// .. determine if bit is set
return isBitSet;
}
Puede usar este código para llamar al método de la misma manera que su código de ejemplo.
¡Espero que ayude! Salud.
private: BitParser() {}
Esto evitará que cualquiera cree instancias.
BitParser() = delete;
transmitir adecuadamente la intención de eliminar el constructor (no solo ocultarlo como private
).
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.
No hay "clase estática" en C ++. El concepto más cercano sería una clase con solo métodos estáticos. Por ejemplo:
// header
class MyClass
{
public :
static void myMethod() ;
} ;
// source
void MyClass::myMethod()
{
// etc.
}
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:
// header
namespace MyNamespace
{
void myMethod() ;
}
// source
namespace MyNamespace
{
void myMethod()
{
// etc.
}
}
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.
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:
// HPP
class Foo
{
public :
void barA() ;
private :
void barB() ;
static std::string myGlobal ;
} ;
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 CPP
void Foo::barA()
{
// I can access Foo::myGlobal
}
void Foo::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
Los espacios de nombres anónimos tendrán la ventaja de hacer que las cosas privadas sean realmente privadas.
Primero, el encabezado HPP
// HPP
namespace Foo
{
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:
// CPP
namespace Foo
{
namespace
{
std::string myGlobal ;
void Foo::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.
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
es private
, incluso si es visible en la private
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! "
¿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() ;
}
struct Beta
{
static void foo() ;
static void bar() ;
};
template <typename T>
struct Gamma
{
void foobar()
{
T::foo() ;
T::bar() ;
}
};
Gamma<alpha> ga ; // compilation error
Gamma<Beta> gb ; // ok
gb.foobar() ; // ok !!!
Porque, si una clase puede ser un parámetro de plantilla, un espacio de nombres no puede.
#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 tus global
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
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex);
}
En BitParser.cpp
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex)
{
//get the bit :)
}
}
En general, esta sería la forma preferida de escribir el código. Cuando no hay necesidad de un objeto, no use una clase.
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 ++
static
200 veces sería algo bueno.
En C ++ desea crear una función estática de una clase (no una clase estática).
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
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.
¿Puedo escribir algo como static class
?
No , de acuerdo con el borrador estándar C ++ 11 N3337 del Anexo C 7.1.1:
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:
static struct 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.
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?
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 de static class
C # en C ++ 11 o posterior:
class BitParser final
{
public:
BitParser() = delete;
static bool GetBitAt(int buffer, int pos);
};
bool BitParser::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:
MyNamespace.Function(blah);
Aquí se explica cómo hacer lo mismo en C ++:
SharedModule.h
class TheDataToBeHidden
{
public:
static int _var1;
static int _var2;
};
namespace SharedData
{
void SetError(const char *Message, const char *Title);
void DisplayError(void);
}
SharedModule.cpp
//Init the data (Link error if not done)
int TheDataToBeHidden::_var1 = 0;
int TheDataToBeHidden::_var2 = 0;
//Implement the namespace
namespace SharedData
{
void SetError(const char *Message, const char *Title)
{
//blah using TheDataToBeHidden::_var1, etc
}
void DisplayError(void)
{
//blah
}
}
OtherFile.h
#include "SharedModule.h"
OtherFile.cpp
//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();
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.
class Class {
public:
void foo() { Static::bar(*this); }
private:
int member{0};
friend class Static;
};
class Static {
public:
template <typename T>
static void bar(T& t) {
t.member = 1;
}
};
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.
class Foo {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
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. .
// C++11 ONLY
class Foo final {
public:
static int someMethod(int someArg);
private:
virtual void __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 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.
// C++11 ONLY
class Foo final {
public:
static int someMethod(int someArg);
private:
// Other private declarations
virtual void __dummy() = 0 final;
}; // Foo now exhibits all the properties of a static class