¿Cómo se crea una clase estática?

Resuelto andrewrk asked hace 16 años • 15 respuestas

¿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;

Suponiendo que creé la BitParserclase. ¿ Cómo sería la BitParserdefinición de clase?

andrewrk avatar Aug 13 '08 06:08 andrewrk
Aceptado

Si está buscando una forma de aplicar la staticpalabra clave a una clase, como puede hacerlo en C#, por ejemplo, no podrá hacerlo sin usar Managed C++.

Pero como se ve en su muestra, solo necesita crear un método estático público en su BitParserobjeto. Al igual que:

BitParser.h

class BitParser
{
public:
  static bool getBitAt(int buffer, int bitIndex);

  // ...

  // Disallow creating an instance of this object
  // (Making all constructors private also works but is not ideal and does not
  // convey your intent as well)
  BitParser() = delete;
};

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.

OJ. avatar Aug 12 '2008 23:08 OJ.

Considere la solución de Matt Price .

  1. En C++, una "clase estática" no tiene significado. Lo más parecido es una clase con sólo métodos y miembros estáticos.
  2. Usar métodos estáticos solo te limitará.

Lo que desea es, expresado en semántica de C++, colocar su función (porque es una función) en un espacio de nombres.

Editar 2011-11-11

No existe una "clase estática" en C++. El concepto más cercano sería una clase con sólo 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 trucos en lenguajes similares a Java (por ejemplo, C#) que no pueden tener funciones que no sean miembros, por lo que deben moverlas dentro de las clases como métodos estáticos.

En C++, lo que realmente quieres es una función que no sea miembro y que declares en un espacio de nombres:

// header
namespace MyNamespace
{
   void myMethod() ;
}

// source
namespace MyNamespace
{
   void myMethod()
   {
      // etc.
   }
}

¿Porqué es eso?

En C++, el espacio de nombres es más poderoso que las clases para el patrón "método estático de Java", porque:

  • Los métodos estáticos tienen acceso a los símbolos privados de las clases.
  • Los métodos estáticos privados todavía son visibles (aunque inaccesibles) para todos, lo que viola un poco 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 mediante 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 ese 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, es necesario utilizar un miembro de datos privados estáticos.

Estoy un poco en desacuerdo, como se muestra a continuación:

La solución "Miembro privado estático"

// 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. Una mirada a la fuente del 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 mire el HPP no podrá (a menos que recurra al sabotaje) acceder Foo::myGlobal.

Pero si lo miras de cerca, encontrarás que es un error colosal: no sólo tu variable privada debe estar declarada en el HPP (y por lo tanto, visible para todo el mundo, a pesar de ser privada), sino que debes declarar en el mismo HPP todas (como en TODAS) las funciones que estarán autorizadas a acceder a él!!!

Entonces, usar un miembro estático privado es como caminar desnudo con la lista de tus amantes tatuada en tu piel: nadie está autorizado a tocar, pero todos pueden mirar. Y la ventaja: todo el mundo puede tener los nombres de las personas autorizadas para jugar en sus retretes.

privatede 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.

// HPP

namespace Foo
{
   void barA() ;
}

Solo para estar seguro de que comentó: no existe ninguna declaración inútil de barB ni de myGlobal. Lo que significa que nadie que lea el encabezado sabe qué se esconde detrás de la barra A.

Entonces, el PCP:

// 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 hacerlo. ¡Y nadie más fuera de este CPP sabe que fooB y myGlobal existen!

A diferencia de la "clase estática" que camina desnuda con su libreta de direcciones tatuada en la 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 (le dejaré, como ejercicio, descubrir cómo se puede acceder a la parte privada de una clase pública usando un truco de comportamiento sucio y no definido...), lo que es, privateincluso privatesi 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, aún debe declararla a todo el mundo modificando el encabezado, lo cual 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 los métodos estáticos de las clases son realmente mejores que los espacios de nombres con funciones que no son miembros?

Cuando necesita 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.

paercebal avatar Sep 21 '2008 22:09 paercebal