Qual é a melhor maneira de inicializar um membro de dados estático privado em C ++? Eu tentei isso no meu arquivo de cabeçalho, mas isso me dá erros estranhos no vinculador:
class foo
{
private:
static int i;
};
int foo::i = 0;
Acho que é porque não consigo inicializar um membro privado de fora da classe. Então, qual é a melhor maneira de fazer isso?
c++
initialization
static-members
Jason Baker
fonte
fonte
inline static int x[] = {1, 2, 3};
. Veja en.cppreference.com/w/cpp/language/static#Static_data_membersRespostas:
A declaração da classe deve estar no arquivo de cabeçalho (ou no arquivo de origem, se não for compartilhado).
Arquivo: foo.h
Mas a inicialização deve estar no arquivo de origem.
Arquivo: foo.cpp
Se a inicialização estiver no arquivo de cabeçalho, cada arquivo que incluir o arquivo de cabeçalho terá uma definição do membro estático. Assim, durante a fase do link, você receberá erros do vinculador, pois o código para inicializar a variável será definido em vários arquivos de origem. A inicialização do
static int i
deve ser feita fora de qualquer função.Nota: Matt Curtis: pontos que C ++ permite a simplificação do acima, se a variável de membro estático é do tipo int const (por exemplo
int
,bool
,char
). Você pode declarar e inicializar a variável de membro diretamente dentro da declaração de classe no arquivo de cabeçalho:fonte
Para uma variável :
foo.h:
foo.cpp:
Isso ocorre porque só pode haver uma instância
foo::i
no seu programa. É o equivalenteextern int i
em um arquivo de cabeçalho eint i
em um arquivo de origem.Para uma constante, você pode colocar o valor diretamente na declaração de classe:
fonte
private
variáveis podem ser inicializadas fora da Classe aqui, isso também pode ser feito para variáveis não estáticas.Class
não faz sentido no Cpp.Desde o C ++ 17, os membros estáticos podem ser definidos no cabeçalho com a palavra-chave inline .
http://en.cppreference.com/w/cpp/language/static
"Um membro de dados estáticos pode ser declarado em linha. Um membro de dados estáticos em linha pode ser definido na definição de classe e pode especificar um inicializador de membro padrão. Ele não precisa de uma definição fora da classe:"
fonte
Para os futuros espectadores desta pergunta, quero ressaltar que você deve evitar o que monkey0506 está sugerindo .
Os arquivos de cabeçalho são para declarações.
Os arquivos de cabeçalho são compilados uma vez para cada
.cpp
arquivo que os direta ou indiretamente#includes
, e o código fora de qualquer função é executado antes da inicialização do programamain()
.Ao colocar:
foo::i = VALUE;
no cabeçalho,foo:i
será atribuído o valorVALUE
(qualquer que seja) a cada.cpp
arquivo, e essas atribuições ocorrerão em uma ordem indeterminada (determinada pelo vinculador) antes damain()
execução.E se formos
#define VALUE
um número diferente em um de nossos.cpp
arquivos? Ele irá compilar bem e não teremos como saber qual deles vence até executar o programa.Nunca coloque o código executado em um cabeçalho pelo mesmo motivo que você nunca possui
#include
um.cpp
arquivo.incluir guardas (que eu concordo que você deve sempre usar) protegem você de algo diferente: o mesmo cabeçalho sendo indiretamente
#include
d várias vezes ao compilar um único.cpp
arquivofonte
Com um compilador da Microsoft [1], variáveis estáticas que não são
int
semelhantes também podem ser definidas em um arquivo de cabeçalho, mas fora da declaração de classe, usando o específico da Microsoft__declspec(selectany)
.Note que não estou dizendo que isso seja bom, apenas digo que isso pode ser feito.
[1] Atualmente, mais compiladores do que o MSC suportam
__declspec(selectany)
- pelo menos gcc e clang. Talvez até mais.fonte
É a sintaxe correta para inicializar a variável, mas ela deve estar no arquivo de origem (.cpp) e não no cabeçalho.
Por ser uma variável estática, o compilador precisa criar apenas uma cópia dela. Você precisa ter uma linha "int foo: i" em algum lugar do código para informar ao compilador onde colocá-lo, caso contrário, você receberá um erro de link. Se isso estiver em um cabeçalho, você receberá uma cópia em cada arquivo que inclui o cabeçalho; portanto, obtenha erros de símbolos definidos multiplicados no vinculador.
fonte
Não tenho representante suficiente aqui para adicionar isso como um comentário, mas, na IMO, é bom estilo escrever seus cabeçalhos com #include guardas de qualquer maneira, o que, como observado por Paranaix há algumas horas, impediria um erro de definição múltipla. A menos que você já esteja usando um arquivo CPP separado, não é necessário usá-lo apenas para inicializar membros não integrais estáticos.
Não vejo necessidade de usar um arquivo CPP separado para isso. Claro, você pode, mas não há nenhuma razão técnica para isso.
fonte
#endif // FOO_H
#pragma once
Se você deseja inicializar algum tipo de composto (string fe), pode fazer algo assim:
Como o método
ListInitializationGuard
é uma variável estáticaSomeClass::getList()
, ele será construído apenas uma vez, o que significa que o construtor é chamado uma vez. Issoinitialize _list
varia para o valor que você precisa. Qualquer chamada subsequente paragetList
simplesmente retornará o_list
objeto já inicializado .Claro que você deve acessar o
_list
objeto sempre chamandogetList()
método.fonte
Padrão de construtor estático C ++ 11 que funciona para vários objetos
Um idioma foi proposto em: https://stackoverflow.com/a/27088552/895245 mas aqui está uma versão mais limpa que não requer a criação de um novo método por membro.
main.cpp
GitHub upstream .
Compile e execute:
Veja também: construtores estáticos em C ++? Eu preciso inicializar objetos estáticos privados
Testado no Ubuntu 19.04.
Variável inline C ++ 17
Mencionado em: https://stackoverflow.com/a/45062055/895245, mas aqui está um exemplo executável de vários arquivos para torná-lo ainda mais claro: como as variáveis embutidas funcionam?
fonte
Você também pode incluir a atribuição no arquivo de cabeçalho se usar proteções de cabeçalho. Eu usei essa técnica para uma biblioteca C ++ que eu criei. Outra maneira de obter o mesmo resultado é usar métodos estáticos. Por exemplo...
O código acima tem o "bônus" de não exigir um arquivo de origem / CPP. Novamente, um método que eu uso para minhas bibliotecas C ++.
fonte
Eu sigo a ideia de Karl. Gosto e agora também o uso. Eu mudei um pouco a notação e adicionei algumas funcionalidades
isso gera
fonte
Também trabalhando no arquivo privateStatic.cpp:
fonte
Que tal um
set_default()
método?Nós apenas teríamos que usar o
set_default(int x)
método e nossastatic
variável seria inicializada.Isso não estaria em desacordo com o restante dos comentários, na verdade, segue o mesmo princípio de inicializar a variável em um escopo global, mas, usando esse método, a tornamos explícita (e fácil de entender) em vez de ter a definição da variável pendurada lá.
fonte
O problema do vinculador que você encontrou provavelmente é causado por:
Este é um problema comum para quem começa com C ++. O membro da classe estática deve ser inicializado na unidade de conversão única, ou seja, no arquivo de origem único.
Infelizmente, o membro estático da classe deve ser inicializado fora do corpo da classe. Isso complica a escrita de código somente de cabeçalho e, portanto, estou usando uma abordagem bem diferente. Você pode fornecer seu objeto estático através da função de classe estática ou não estática, por exemplo:
fonte
Uma maneira "antiga" de definir constantes é substituí-las por
enum
:Dessa forma, não é necessário fornecer uma definição e evita fazer o valor l constante , o que pode poupar algumas dores de cabeça, por exemplo, quando você acidentalmente usa o ODR .
fonte
Eu só queria mencionar algo um pouco estranho para mim quando encontrei isso pela primeira vez.
Eu precisava inicializar um membro de dados estático privado em uma classe de modelo.
no .h ou .hpp, é algo parecido com isto para inicializar um membro de dados estático de uma classe de modelo:
fonte
Isso serve ao seu propósito?
fonte