Eu quero ter uma classe com um membro de dados estático privado (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 qualquer instância da classe e configurar os membros de dados estáticos da classe. Ele é executado apenas uma vez (como as variáveis são somente leitura e precisam ser definidas apenas uma vez) e, como é uma função da classe, ele pode acessar seus membros privados. Eu poderia adicionar código no construtor que verifica se o vetor foi inicializado e inicializá-lo se não for, mas que introduz muitas verificações necessárias e não parece ser a solução ideal para o problema.
Ocorre-me que, uma vez que as variáveis serão apenas de leitura, elas podem ser apenas constantes estáticas públicas, para que eu possa defini-las uma vez fora da classe, mas mais uma vez, parece um hack feio.
É possível ter membros de dados estáticos privados em uma classe se não quiser inicializá-los no construtor de instância?
fonte
Respostas:
Para obter o equivalente a um construtor estático, você precisa escrever uma classe comum separada para armazenar os dados estáticos e, em seguida, criar uma instância estática dessa classe comum.
fonte
friend
faz muito sentido para que a classeElsewhere
possa acessar facilmenteStaticStuff
os internos (sem quebrar o encapsulamento de nenhuma maneira perigosa, devo acrescentar).Bem, você pode ter
Não esqueça (no .cpp) isto:
O programa ainda será vinculado sem a segunda linha, mas o inicializador não será executado.
fonte
MyClass::a.push_back(i)
vez dea.push_back(i)
?_initializer
é um subobjeto deMyClass
. Os subobjetos são inicializados nesta ordem: subobjetos da classe base virtual, em ordem de profundidade, da esquerda para a direita (mas apenas inicializando cada subobjeto distinto uma vez); subobjetos simples da classe base, em ordem de profundidade, da esquerda para a direita; em seguida, subobjetos membros em ordem de declaração. Portanto, é seguro usar a estratégia do EFraim, desde que o código_initialiser
se refira apenas aos membros declarados antes dele.Solução C ++ 11
Desde o C ++ 11, você pode simplesmente usar expressões lambda para inicializar membros de classe estáticos. Isso funciona mesmo se você precisar impor uma ordem de construção entre os vários membros estáticos ou se você tiver membros estáticos
const
.Arquivo de cabeçalho:
Arquivo fonte:
fonte
try catch
bloco se houver exceções.No arquivo .h:
No arquivo .cpp:
fonte
Aqui está outra abordagem semelhante à de Daniel Earwicker, também usando a sugestão de classe de amigo de Konrad Rudolph. Aqui, usamos uma classe de utilidade privada privada para inicializar os membros estáticos da sua classe principal. Por exemplo:
Arquivo de cabeçalho:
Arquivo de implementação:
Essa abordagem tem a vantagem de ocultar completamente a classe Initializer do mundo exterior, mantendo tudo o que está contido na classe a ser inicializado.
fonte
ToBeInitialized::Initializer::Initializer()
é chamado, portanto, é necessário adicionarToBeInitialized::Initializer ToBeInitialized::initializer;
ao arquivo de implementação. Peguei algumas coisas da sua ideia e da EFraim, e funciona exatamente como eu preciso e parece limpo. Obrigado cara.Test::StaticTest()
é chamado exatamente uma vez durante a inicialização estática global.O chamador precisa adicionar apenas uma linha à função que deve ser seu construtor estático.
static_constructor<&Test::StaticTest>::c;
força a inicialização dec
durante a inicialização estática global.fonte
Não é necessário criar uma
init()
função astd::vector
partir de um intervalo:Observe, no entanto, que as estáticas do tipo de classe causam problemas nas bibliotecas, portanto devem ser evitadas lá.
Atualização do C ++ 11
A partir do C ++ 11, você pode fazer isso:
É semanticamente equivalente à solução C ++ 98 na resposta original, mas você não pode usar uma string literal no lado direito, portanto não é completamente superior. No entanto, se tiver um vector de qualquer outro tipo de
char
,wchar_t
,char16_t
ouchar32_t
(matrizes das quais podem ser escritas como sequências de caracteres), a versão C ++ 11 irá remover rigorosamente código padrão sem introduzir outra sintaxe, em comparação com o C ++ 98 versão.fonte
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.
fonte
Ao tentar compilar e usar a classe
Elsewhere
( da resposta de Earwicker ), recebo:Parece que não é possível inicializar atributos estáticos de tipos não inteiros sem colocar algum código fora da definição de classe (CPP).
Para fazer essa compilação, você pode usar " um método estático com uma variável local estática dentro ". Algo assim:
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 sintaxe muda um pouco, mas ainda é útil. Espero que isso seja útil para alguém,
Hugo González Castro.
fonte
Eu acho que a solução simples para isso será:
fonte
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 - decidi que não quero chamar o ctor de RandClass (), a menos que eu o use ... é por isso que não quero inicializar singleton globalmente no meu código. Também adicionei uma interface simples no meu caso.
Aqui está o código final:
Simplifiquei o código e uso a função rand () e seu único inicializador srand ()
fonte
Aqui está minha variante da solução da EFraim; a diferença é que, graças à instanciação implícita do modelo, o construtor estático é chamado apenas se instâncias da classe são criadas e que nenhuma definição no
.cpp
arquivo é necessária (graças à mágica da instanciação do modelo).No
.h
arquivo, você tem:No
.cpp
arquivo, você pode ter:Observe que
MyClass::a
é inicializado apenas se a linha [1] estiver lá, porque isso chama (e requer instanciação) do construtor, que requer instanciação de_initializer
.fonte
Aqui está outro método, em que o vetor é privado para o arquivo que contém a implementação usando um espaço para nome anônimo. É útil para coisas como tabelas de pesquisa que são privadas para a implementação:
fonte
I
ei
algo um pouco mais obscuro para não usá-los acidentalmente em algum lugar mais baixo do arquivo.Certamente não precisa ser tão complicado quanto a resposta atualmente aceita (por Daniel Earwicker). A turma é supérflua. Não há necessidade de uma guerra de idiomas neste caso.
Arquivo .hpp:
arquivo .cpp:
fonte
Ofertas do GCC
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
Identifique um método estático com esse atributo e ele será executado no carregamento do módulo, antes de main ().
fonte
Você define variáveis de membro estáticas da mesma forma que define métodos de membro.
foo.h
foo.cpp
fonte
Para inicializar uma variável estática, basta fazê-lo dentro de um arquivo de origem. Por exemplo:
fonte
Que tal criar um modelo para imitar o comportamento do C #.
fonte
Para casos simples como aqui, uma variável estática envolvida dentro de uma função de membro estática é quase tão boa. É simples e geralmente será otimizado pelos compiladores. Porém, isso não resolve o problema da ordem de inicialização de objetos complexos.
fonte
Isso é uma solução?
fonte
Um construtor estático pode ser emulado usando uma classe de amigo ou classe aninhada como abaixo.
Resultado:
fonte
new
inserindo uma matriz de caracteres apenas para vazar imediatamente o ponteiro e substituí-lo !?Uau, não acredito que ninguém tenha mencionado a resposta mais óbvia e que imita mais de perto o comportamento do construtor estático do C #, ou seja, ele não é chamado até que o primeiro objeto desse tipo seja criado.
std::call_once()
está disponível no C ++ 11; se você não pode usar isso, isso pode ser feito com uma variável de classe booleana estática e uma operação atômica de comparação e troca. No seu construtor, veja se você pode alterar atomicamente o sinalizador estático de classe defalse
paratrue
e, se sim, pode executar o código de construção estática.Para obter crédito extra, torne-o um sinalizador de três vias em vez de um booleano, ou seja, não execute, execute e execute. Todas as outras instâncias dessa classe podem girar com trava até que a instância que está executando o estático-construtor tenha terminado (por exemplo, emita um limite de memória e defina o estado como "concluído a execução"). Seu bloqueio de rotação deve executar a instrução de "pausa" do processador, dobrar a espera de cada vez até um limite, etc. - técnica bastante comum de bloqueio de rotação.
Na ausência do C ++ 11, isso deve começar.
Aqui estão alguns pseudocódigo para guiá-lo. Coloque isso na sua definição de classe:
E isso no seu construtor:
fonte