construtores estáticos em C ++? Eu preciso inicializar objects estáticos privados

Eu quero ter uma class com um membro de dados estáticos privados (um vetor que contém todos os caracteres az). Em java ou c #, posso apenas criar um “construtor estático” que será executado antes de criar quaisquer instâncias da class e configurar os membros de dados estáticos da class. Ele só é executado uma vez (já que as variables ​​são somente de leitura e precisam ser configuradas apenas uma vez) e, como é uma function da class, ele pode acessar seus membros privados. Eu poderia adicionar código no construtor que verifica se o vetor é inicializado e inicializá-lo se não for, mas isso introduz muitas verificações necessárias e não parece ser a solução ideal para o problema.

O pensamento me ocorre que, como as variables ​​serão somente de leitura, elas podem ser apenas static public const, então eu posso defini-las uma vez fora da class, mas mais uma vez, parece uma espécie de hack feio.

É possível ter membros de dados estáticos privados em uma class, se eu não quiser inicializá-los no construtor da instância?

Para obter o equivalente de um construtor estático, você precisa escrever uma class ordinária separada para armazenar os dados estáticos e, em seguida, criar uma instância estática dessa class ordinária.

class StaticStuff { std::vector letters_; public: StaticStuff() { for (char c = 'a'; c < = 'z'; c++) letters_.push_back(c); } // provide some way to get at letters_ }; class Elsewhere { static StaticStuff staticStuff; // constructor runs once, single instance }; 

Bem, você pode ter

 class MyClass { public: static vector a; static class _init { public: _init() { for(char i='a'; i< ='z'; i++) a.push_back(i); } } _initializer; }; 

Não esqueça (no .cpp) isto:

 vector MyClass::a; MyClass::_init MyClass::_initializer; 

O programa continuará vinculando sem a segunda linha, mas o inicializador não será executado.

No arquivo .h:

 class MyClass { private: static int myValue; }; 

No arquivo .cpp:

 #include "myclass.h" int MyClass::myValue = 0; 

Aqui está outra abordagem semelhante à de Daniel Earwicker, também usando a sugestão de class de amigo de Konrad Rudolph. Aqui, usamos uma class interna de utilitários de amigos privados para inicializar os membros estáticos da sua class principal. Por exemplo:

Arquivo de header:

 class ToBeInitialized { // Inner friend utility class to initialize whatever you need class Initializer { public: Initializer(); }; friend class Initializer; // Static member variables of ToBeInitialized class static const int numberOfFloats; static float *theFloats; // Static instance of Initializer // When this is created, its constructor initializes // the ToBeInitialized class' static variables static Initializer initializer; }; 

Arquivo de implementação:

 // Normal static scalar initializer const int ToBeInitialized::numberOfFloats = 17; // Constructor of Initializer class. // Here is where you can initialize any static members // of the enclosing ToBeInitialized class since this inner // class is a friend of it. ToBeInitialized::Initializer::Initializer() { ToBeInitialized::theFloats = (float *)malloc(ToBeInitialized::numberOfFloats * sizeof(float)); for (int i = 0; i < ToBeInitialized::numberOfFloats; ++i) ToBeInitialized::theFloats[i] = calculateSomeFancyValue(i); } 

Essa abordagem tem a vantagem de ocultar completamente a class Initializer do mundo externo, mantendo tudo o que está dentro da class para ser inicializado.

Se a ordem de construção estática não for relevante

Se você não precisa impor uma ordem de construção estática entre os membros, simplesmente use inicializadores lambda (desde C ++ 11).

Arquivo de header:

 class Foo { static Bar staticMember; }; 

Arquivo fonte:

 Bar Foo::staticMember = [] { Bar bar; bar = ... ; return bar; }(); 

Se a ordem de construção estática for relevante

Se você precisar acessar um membro estático durante a construção de outro, poderá usar o seguinte padrão de construção estático (desde C ++ 11). Nenhuma class auxiliar é necessária.

Arquivo de header:

 class Foo { static Bar staticMember1; static Bar staticMember2; static const bool StaticCtor; // Note: must be the last static member }; 

Arquivo fonte:

 Bar Foo::staticMember1; Bar Foo::staticMember2; const bool Foo::StaticCtor = [] { staticMember1 = ... ; staticMember2 = ... ; return true; // Note: unused dummy return value }(); 

Test::StaticTest() é chamado exatamente uma vez durante a boot estática global.

O chamador só precisa adicionar uma linha à function que será seu construtor estático.

static_constructor< &Test::StaticTest>::c; força a boot de c durante a boot estática global.

 template struct static_constructor { struct constructor { constructor() { ctor(); } }; static constructor c; }; template typename static_constructor::constructor static_constructor::c; ///////////////////////////// struct Test { static int number; static void StaticTest() { static_constructor< &Test::StaticTest>::c; number = 123; cout < < "static ctor" << endl; } }; int Test::number; int main(int argc, char *argv[]) { cout << Test::number << endl; return 0; } 

Não há necessidade de uma function init() , std::vector pode ser criado a partir de um intervalo:

 // h file: class MyClass { static std::vector alphabet; // ... }; // cpp file: #include  static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; std::vector MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) ); 

Note, no entanto, que a estática do tipo de class causa problemas nas bibliotecas, portanto elas devem ser evitadas lá.

Atualização do C ++ 11

A partir do C ++ 11, você pode fazer isso:

 // cpp file: std::vector MyClass::alphabet = { 'a', 'b', 'c', ..., 'z' }; 

É semanticamente equivalente à solução C ++ 98 na resposta original, mas você não pode usar uma string literal no lado direito, então não é completamente superior. No entanto, se você tiver um vetor de qualquer outro tipo que não char , wchar_t , char16_t ou char32_t (matrizes dos quais podem ser escritas como literais de string), a versão C + + 11 removerá estritamente o código padrão sem introduzir outra syntax, comparado com o Versão C ++ 98.

O conceito de construtores estáticos foi introduzido em Java depois que eles aprenderam com os problemas em C ++. Portanto, não temos equivalente direto.

A melhor solução é usar tipos de POD que podem ser inicializados explicitamente.
Ou torne seus membros estáticos um tipo específico que tenha seu próprio construtor que irá inicializá-lo corretamente.

 //header class A { // Make sure this is private so that nobody can missues the fact that // you are overriding std::vector. Just doing it here as a quicky example // don't take it as a recomendation for deriving from vector. class MyInitedVar: public std::vector { public: MyInitedVar() { // Pre-Initialize the vector. for(char c = 'a';c < = 'z';++c) { push_back(c); } } }; static int count; static MyInitedVar var1; }; //source int A::count = 0; A::MyInitedVar A::var1; 

Ao tentar compilar e usar a class Elsewhere ( da resposta de Earwicker ) eu recebo:

 error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A) 

Parece não é possível inicializar atributos estáticos de tipos não inteiros sem colocar algum código fora da definição de class (CPP).

Para fazer essa compilation, você pode usar ” um método estático com uma variável local estática dentro “. Algo assim:

 class Elsewhere { public: static StaticStuff& GetStaticStuff() { static StaticStuff staticStuff; // constructor runs once, single instance return staticStuff; } }; 

E você também pode passar argumentos para o construtor ou inicializá-lo com valores específicos, é muito flexível, poderoso e fácil de implementar … a única coisa é que você tem um método estático contendo uma variável estática, não um atributo estático … A syntax muda um pouco, mas ainda é útil. Espero que isso seja útil para alguém

Hugo González Castro.

Eu acho que solução simples para isso será:

  //Xh #pragma once class X { public: X(void); ~X(void); private: static bool IsInit; static bool Init(); }; //X.cpp #include "Xh" #include  X::X(void) { } X::~X(void) { } bool X::IsInit(Init()); bool X::Init() { std::cout< < "ddddd"; return true; } // main.cpp #include "Xh" int main () { return 0; } 

Apenas resolvi o mesmo truque. Eu tive que especificar a definição de um único membro estático para Singleton. Mas torne as coisas mais complicadas – eu decidi que não quero chamar o ctor de RandClass () a menos que eu vá usá-lo … é por isso que eu não queria inicializar o singleton globalmente no meu código. Também adicionei interface simples no meu caso.

Aqui está o código final:

Eu simplifiquei o código e usei a function rand () e seu único seedzer initialand srand ()

 interface IRandClass { public: virtual int GetRandom() = 0; }; class RandClassSingleton { private: class RandClass : public IRandClass { public: RandClass() { srand(GetTickCount()); }; virtual int GetRandom(){return rand();}; }; RandClassSingleton(){}; RandClassSingleton(const RandClassSingleton&); // static RandClass m_Instance; // If you declare m_Instance here you need to place // definition for this static object somewhere in your cpp code as // RandClassSingleton::RandClass RandClassSingleton::m_Instance; public: static RandClass& GetInstance() { // Much better to instantiate m_Instance here (inside of static function). // Instantiated only if this function is called. static RandClass m_Instance; return m_Instance; }; }; main() { // Late binding. Calling RandClass ctor only now IRandClass *p = &RandClassSingleton::GetInstance(); int randValue = p->GetRandom(); } abc() { IRandClass *same_p = &RandClassSingleton::GetInstance(); }