Como você cria uma classe estática em C ++?


264

Como você cria uma classe estática em C ++? Eu deveria ser capaz de fazer algo como:

cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;

Supondo que eu criei a BitParserclasse. Como seria a BitParserdefinição de classe?


198
Sim. Nos velhos tempos, chamamos isso de "função". Vocês crianças hoje com seus "namespaces" malucos. Ei, saia do meu gramado! <shakes fist>
Vagrant

7
@ Uma função dentro de um namespace ainda é uma função. Uma função que pertence a uma classe é chamada de método. Se for um método estático, você o chamará da mesma forma como se fosse uma função dentro de um espaço para nome.

48
5 anos depois e agora estou do lado do @Vagrant. Que pergunta boba!
andrewrk

16
9 anos depois e agora eu tenho a minha própria linguagem de programação que não tem sequer classes: ziglang.org
andrewrk

1
Classes de contêineres do IMO (com apenas métodos estáticos) são úteis em certos casos.
AarCee 17/05/19

Respostas:


272

Se você está procurando uma maneira de aplicar a palavra-chave "estática" a uma classe, como é possível em C #, por exemplo, não será possível sem usar o C ++ gerenciado.

Mas, pela aparência do seu exemplo, você só precisa criar um método estático público no seu objeto BitParser. Igual a:

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

Você pode usar esse código para chamar o método da mesma maneira que seu código de exemplo.

Espero que ajude! Felicidades.


2
JO, você tem um erro de sintaxe . A palavra-chave estática deve ser usada apenas na definição de classe, e não na definição de método.
andrewrk

90
Para deixar sua intenção clara nessa abordagem, você pode usar adicionalmente um construtor privado. private: BitParser() {}Isso impedirá que alguém crie instâncias.
Danvil 22/07/10

7
A segurança do encadeamento @MoatazElmasry é um problema quando você compartilha o estado. Na implementação acima não é compartilhado nenhum estado, de modo que não pode haver qualquer problema com a segurança do thread ... a menos que você está suficientemente estúpido para usar estática dentro essas funções. Então, sim, o código acima é seguro para threads, apenas mantenha o estado persistente fora de suas funções e você estará bem.
JO.

@MoatazElmasry Incorrect. Dois segmentos não podem modificar variáveis ​​locais não estáticas em uma função estática.
JO.

12
Se C ++ 11, eu diria que é melhor BitParser() = delete;transmitir adequadamente a intenção de remover o construtor (não apenas ocultando-o como private).
Phoenix #:

247

Considere a solução de Matt Price .

  1. No C ++, uma "classe estática" não tem significado. A coisa mais próxima é uma classe com apenas métodos e membros estáticos.
  2. O uso de métodos estáticos apenas o limitará.

O que você deseja é, expresso na semântica do C ++, colocar sua função (pois é uma função) em um espaço para nome.

Edit 2011-11-11

Não há "classe estática" em C ++. O conceito mais próximo seria uma classe com apenas métodos estáticos. Por exemplo:

// header
class MyClass
{
   public :
      static void myMethod() ;
} ;

// source
void MyClass::myMethod()
{
   // etc.
}

Mas você deve se lembrar que "classes estáticas" são hacks no tipo de linguagem Java (por exemplo, C #) que são incapazes de ter funções não pertencentes a membros; portanto, eles precisam movê-las dentro das classes como métodos estáticos.

No C ++, o que você realmente deseja é uma função que não seja membro que você declarará em um espaço para nome:

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

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

Por que é que?

No C ++, o espaço para nome é mais poderoso que as classes para o padrão "método estático Java", porque:

  • métodos estáticos têm acesso aos símbolos particulares das classes
  • métodos estáticos privados ainda são visíveis (se inacessíveis) para todos, o que viola um pouco o encapsulamento
  • métodos estáticos não podem ser declarados para a frente
  • métodos estáticos não podem ser sobrecarregados pelo usuário da classe sem modificar o cabeçalho da biblioteca
  • não há nada que possa ser feito por um método estático que não possa ser melhor do que uma função não membro (possivelmente amigo) no mesmo espaço de nome
  • os espaços para nome têm sua própria semântica (eles podem ser combinados, podem ser anônimos etc.)
  • etc.

Conclusão: Não copie / cole esse padrão Java / C # em C ++. Em Java / C #, o padrão é obrigatório. Mas em C ++, é um estilo ruim.

Edit 2010-06-10

Houve um argumento a favor do método estático porque, às vezes, é necessário usar uma variável de membro privado estático.

Discordo um pouco, como mostra a seguir:

A solução "Membro particular estático"

// HPP

class Foo
{
   public :
      void barA() ;
   private :
      void barB() ;
      static std::string myGlobal ;
} ;

Primeiro, myGlobal é chamado myGlobal porque ainda é uma variável privada global. Uma olhada na fonte do CPP esclarecerá 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 !!!
}

À primeira vista, o fato de a função livre barC não poder acessar Foo :: myGlobal parece uma coisa boa do ponto de vista do encapsulamento ... É legal porque alguém olhando para o HPP não poderá (a menos que recorra à sabotagem) acessar Foo :: myGlobal.

Mas se você olhar atentamente, verá que é um erro colossal: não apenas sua variável privada ainda deve ser declarada no HPP (e, portanto, visível para todo o mundo, apesar de ser privado), mas você deve declarar no mesmo HPP todas as funções (como em TODAS) que serão autorizadas a acessá-lo !!!

Portanto, usar um membro estático privado é como andar nu, com a lista de seus amantes tatuados na pele: ninguém está autorizado a tocar, mas todos podem espiar. E o bônus: todos podem ter o nome daqueles autorizados a jogar com suas privadas.

private de fato ... :-D

A solução "namespaces anônimos"

Os namespaces anônimos terão a vantagem de tornar as coisas privadas realmente privadas.

Primeiro, o cabeçalho HPP

// HPP

namespace Foo
{
   void barA() ;
}

Apenas para ter certeza de que você observou: Não há declaração inútil de barB nem myGlobal. O que significa que ninguém lendo o cabeçalho sabe o que está escondido atrás da barraA.

Então, o 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 você pode ver, como a chamada declaração de "classe estática", fooA e fooB ainda podem acessar o myGlobal. Mas ninguém mais pode. E ninguém mais fora deste CPP sabe que o foG e o myGlobal existem!

Ao contrário da "classe estática" caminhando nua com o catálogo de endereços tatuado na pele, o espaço para nome "anônimo" está totalmente vestido , o que parece um AFAIK muito melhor encapsulado.

Isso realmente importa?

A menos que os usuários do seu código sejam sabotadores (deixarei você, como exercício, descobrir como alguém pode acessar a parte privada de uma classe pública usando um truque sujo e indefinido ...), o que privateé private, mesmo que seja é visível noprivate seção de uma classe declarada em um cabeçalho.

Ainda assim, se você precisar adicionar outra "função privada" com acesso ao membro privado, ainda deverá declará-la para todo o mundo modificando o cabeçalho, o que é um paradoxo para mim: se eu alterar a implementação de meu código (a parte CPP), a interface (a parte HPP) NÃO deve mudar. Citando Leonidas: " Isso é ENCAPSULAÇÃO! "

Edit 20-09-2014

Quando os métodos estáticos das classes são realmente melhores que os namespaces com funções que não são membros?

Quando você precisar agrupar funções e alimentar esse grupo em um modelo:

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, se uma classe pode ser um parâmetro de modelo, os espaços para nome não podem.


3
O GCC suporta -fno-access-control, que pode ser usado em testes de unidade de caixa branca para acessar os membros da classe privada. Essa é a única razão pela qual posso justificar o uso de um membro da classe em vez de um global anônimo / estático na implementação.
Tom

8
@ Tom: Uma solução multi-plataforma seria adicionar o seguinte código #define private publicnos cabeçalhos ... ^ _ ^ ...
paercebal

1
@ Tom: de qualquer maneira, IMHO, mesmo considerando o teste de unidade, os contras de "mostrar muita coisa" supera os profissionais. Eu acho que uma solução alternativa seria colocar o código a ser testado em uma função usando os parâmetros necessários (e não mais) em um utilitiesespaço para nome. Desta forma, esta função pode ser testado unidades, e ainda não têm acesso especial para os membros privados (como eles são dados como parâmetros na chamada de função) ...
paercebal

@ paercebal Estou prestes a pular a bordo do seu navio, mas tenho uma reserva final. Se alguém pular na sua namespacevontade, não terá acesso aos seus globalmembros, embora ocultos? Obviamente, eles teriam que adivinhar, mas, a menos que você esteja ofuscando seu código intencionalmente, nomes de variáveis ​​são muito fáceis de adivinhar.
Zak

@Zak: De fato, eles poderiam, mas apenas tentando fazê-lo no arquivo CPP onde a variável myGlobal é declarada. O ponto é mais visibilidade do que acessibilidade. Na classe estática, a variável myGlobal é privada, mas ainda visível. Isso não é tão importante quanto parece, mas ainda assim, em uma DLL, mostrar um símbolo que deve ser privado para a DLL em um cabeçalho exportado pode ser estranho ... No espaço para nome, o myGlobal existe apenas no arquivo CPP (você pode ir ainda mais longe e torná-lo estático). Essa variável não aparece nos cabeçalhos públicos.
paercebal

63

Você também pode criar uma função livre em um espaço para nome:

No BitParser.h

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex);
}

No BitParser.cpp

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex)
    {
        //get the bit :)
    }
}

Em geral, essa seria a maneira preferida de escrever o código. Quando não há necessidade de um objeto, não use uma classe.


1
Em alguns casos, você pode querer ter um encapsulamento de dados, mesmo que a classe seja principalmente "estática". Os alunos de classe particular estática darão a você isso. Os membros do espaço para nome são sempre públicos e não podem fornecer o encapsulamento de dados.
Torleif

Se o var "membro" for declarado e acessado apenas a partir do arquivo .cpp, será mais privado que um var privado declarado no arquivo .h. NÃO que eu recomende esta técnica.
precisa saber é o seguinte

3
@ Torleif: Você está errado. namespaces são melhores para encapsulamento do que membros privados estáticos. Veja minha resposta para demonstração.
paercebal

1
sim, mas no espaço para nome você deve manter a ordem da função, ao contrário da classe com membros estáticos, por exemplo, void a () {b ();} b () {} geraria um erro em um espaço para nome, mas não em uma classe com membros estáticos
Moataz Elmasry

13

Se você está procurando uma maneira de aplicar a palavra-chave "estática" a uma classe, como em C #, por exemplo

classes estáticas são apenas o compilador que o mantém em mãos e impede que você escreva quaisquer métodos / variáveis ​​de instância.

Se você acabou de escrever uma classe normal sem nenhum método / variável de instância, é a mesma coisa, e é isso que você faria em C ++


Não para reclamar (especialmente para você), mas um compilador de mãos dadas para me impedir de escrever ou cortar / colar a palavra static200 vezes seria uma coisa boa.
3Dave

Concordou - mas uma classe estática em C # também não faz isso. Ele só não consegue compilar quando você esquecer de colar estática lá :-)
Orion Edwards

Sim - justo o suficiente. Minhas macros estão aparecendo. Honestamente, se eu declarar a classe como estática, o compilador só deve gerar um erro se tentar instancia-la. Regras que exigem que eu me repita são desagradáveis ​​e devem ser as primeiras contra a parede quando a revolução chegar.
3Dave

11

No C ++, você deseja criar uma função estática de uma classe (não uma classe estática).

class BitParser {
public:
  ...
  static ... getBitAt(...) {
  }
};

Você poderá chamar a função usando BitParser :: getBitAt () sem instanciar um objeto que eu presumo ser o resultado desejado.


11

Posso escrever algo como static class ?

Não , de acordo com o rascunho padrão do C ++ 11 N3337 anexo C 7.1.1 da :

Alteração: No C ++, os especificadores estáticos ou externos podem ser aplicados apenas a nomes de objetos ou funções. O uso desses especificadores com declarações de tipo é ilegal no C ++. Em C, esses especificadores são ignorados quando usados ​​em declarações de tipo. Exemplo:

static struct S {    // valid C, invalid in C++
  int i;
};

Justificativa: os especificadores de classe de armazenamento não têm significado quando associados a um tipo. No C ++, os membros da classe podem ser declarados com o especificador de classe de armazenamento estático. Permitir especificadores de classe de armazenamento nas declarações de tipo pode tornar o código confuso para os usuários.

E tipo struct,class também é uma declaração de tipo.

O mesmo pode ser deduzido percorrendo a árvore da sintaxe no Anexo A.

É interessante notar que isso static structera legal em C, mas não teve efeito: por que e quando usar estruturas estáticas na programação C?


6

Como foi observado aqui, uma maneira melhor de conseguir isso em C ++ pode estar usando namespaces. Mas como ninguém mencionou a finalpalavra - chave aqui, estou publicando como seria um equivalente direto static classdo C # no C ++ 11 ou posterior:

class BitParser final
{
public:
  BitParser() = delete;

  static bool GetBitAt(int buffer, int pos);
};

bool BitParser::GetBitAt(int buffer, int pos)
{
  // your code
}

5

Você 'pode' ter uma classe estática em C ++, como mencionado anteriormente, uma classe estática é aquela que não possui nenhum objeto dela instanciada. Em C ++, isso pode ser obtido declarando o construtor / destruidor como privado. O resultado final é o mesmo.


O que você está sugerindo pode criar uma classe singleton, mas não é o mesmo que uma classe estática.
ksinkar

4

No C ++ gerenciado, a sintaxe da classe estática é: -

public ref class BitParser abstract sealed
{
    public:
        static bool GetBitAt(...)
        {
            ...
        }
}

... Antes tarde do que nunca...


3

Isso é semelhante à maneira do C # de fazer isso em C ++

Em C # file.cs, você pode ter var privado dentro de uma função pública. Quando em outro arquivo, você pode usá-lo chamando o namespace com a função como em:

MyNamespace.Function(blah);

Veja como calcular o mesmo em 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();

2
Mas todos Cango para TheDataToBeHidden -> Não é uma solução
Guy L

3

Diferente de outra linguagem de programação gerenciada, "classe estática" NÃO tem significado em C ++. Você pode fazer uso da função de membro estático.


0

Um caso em que os namespaces podem não ser tão úteis para obter "classes estáticas" é quando essas classes são usadas para obter composição sobre herança. Os espaços para nome não podem ser amigos das classes e, portanto, não podem acessar membros particulares de uma classe.

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

0

Uma (das muitas) alternativas, mas a mais (na minha opinião) mais elegante (em comparação ao uso de namespaces e construtores privados para emular o comportamento estático), a maneira de obter o comportamento de "classe que não pode ser instanciada" no C ++ seria: declare uma função virtual pura simulada com o privatemodificador de acesso.

class Foo {
   public:
     static int someMethod(int someArg);

   private:
     virtual void __dummy() = 0;
};

Se você estiver usando C ++ 11, poderá percorrer uma milha extra para garantir que a classe não seja herdada (para emular puramente o comportamento de uma classe estática) usando o finalespecificador na declaração de classe para impedir que outras classes a herdem. .

// C++11 ONLY
class Foo final {
   public:
     static int someMethod(int someArg);

   private:
      virtual void __dummy() = 0;
};

Por mais bobo e ilógico que possa parecer, o C ++ 11 permite a declaração de uma "função virtual pura que não pode ser substituída", que você pode usar juntamente com a declaração da classe finalpara implementar pura e completamente o comportamento estático, pois isso resulta na resultante classe para não ser herdável e a função fictícia para não ser substituída de forma alguma.

// 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.