Pergunta

garantias C ++ que variáveis ??em um (arquivo .cpp) unidade de compilação são inicializados em ordem de declaração. Para o número de unidades de compilação essa regra funciona para cada um separadamente (variáveis ??I média estáticos fora das aulas).

Mas, a ordem de inicialização de variáveis, é indefinido em diferentes unidades de compilação.

Onde posso ver algumas explicações sobre este fim de gcc e MSVC (eu sei que depender de que é uma idéia muito ruim - é apenas para entender os problemas que possa ter com código legado quando se deslocam para o novo GCC maior e OS diferente)? ??

Foi útil?

Solução

Como você diz a ordem é indefinida em diferentes unidades de compilação.

Dentro da mesma unidade de compilação a ordem é bem definida:. A mesma ordem que a definição

Isto é porque este não for resolvido no nível da linguagem, mas no nível vinculador. Então, você realmente precisa verificar a documentação vinculador. Embora eu realmente duvido que isso vai ajudar de alguma forma útil.

Para gcc: Confira ld

Eu descobri que mesmo mudando a ordem dos arquivos objetos que estão sendo vinculado pode alterar a ordem de inicialização. Portanto, não é apenas o seu vinculador que você precisa se preocupar, mas como o vinculador é chamado pelo seu sistema de compilação. Mesmo tentar resolver o problema é praticamente um não arranque.

Esta é geralmente apenas um problema ao inicializar globals que fazem referência um ao outro durante a sua própria inicialização (por isso só afeta objetos com construtores).

Existem técnicas para contornar o problema.

  • A inicialização lenta.
  • Schwarz Contador
  • Coloque todas as variáveis ??globais complexos dentro de uma mesma unidade de compilação.

  • Nota 1: globals:
    Usado livremente para se referir a variáveis ??duração de armazenamento estático que são potencialmente inicializados antes main().
  • Nota 2: Potencialmente
    No caso geral, esperamos variáveis ??duração de armazenagem estática para ser inicializado antes principal, mas o compilador é permitido inicialização adiar em algumas situações (as regras são Standard VER complexo para detalhes).

Outras dicas

Espero que o fim do construtor entre os módulos é principalmente uma função de que ordem você passar os objetos para o vinculador.

No entanto, GCC faz deixá-lo uso init_priority para especificar explicitamente a ordenação para ctors globais:

class Thingy
{
public:
    Thingy(char*p) {printf(p);}
};

Thingy a("A");
Thingy b("B");
Thingy c("C");

saídas 'ABC' como seria de esperar, mas

Thingy a __attribute__((init_priority(300))) ("A");
Thingy b __attribute__((init_priority(200))) ("B");
Thingy c __attribute__((init_priority(400))) ("C");

saídas 'BAC'.

Uma vez que você já sabe que você não deve confiar nesta informação menos que seja absolutamente necessário, aqui se trata. Minha observação geral em vários toolchains (MSVC, gcc / ld, clang / llvm, etc) é que a ordem em que seus arquivos de objetos são passados ??para o vinculador é a ordem em que eles serão inicializados.

Há exceções para isso, e eu não reivindicamos a todos eles, mas aqui são os que eu corri para dentro de mim:

1) CCG versões anteriores a 4,7, na verdade, de inicialização na ordem inversa da sua linha de ligação. Este bilhete no GCC é quando a mudança aconteceu, e quebrou um monte de programas que dependiam de ordem de inicialização (incluindo o meu!).

2) Em GCC e Clang, o uso de construtor prioridade função pode alterar a ordem de inicialização. Note que isto só se aplica a funções que são declarados "construtores" (ou seja, eles devem ser executados apenas como um construtor do objeto global seria). Eu tentei usar prioridades como este e descobriu que, mesmo com a mais alta prioridade em uma função de construtor, todos os construtores sem prioridade (objetos globais por exemplo normais, funções de construtor sem prioridade) serão inicializados início . Em outras palavras, a prioridade é apenas relativa a outras funções com as prioridades, mas os verdadeiros cidadãos de primeira classe são aqueles sem prioridade. Para piorar as coisas, esta regra é efetivamente o oposto em GCC antes para 4,7 devido a ponto (1) acima.

3) No Windows, há um compartilhada-biblioteca muito limpo e útil função (DLL) ponto de entrada chamada DllMain () , que se definido, será executado com o parâmetro 'fdwReason' igual DLL_PROCESS_ATTACH diretamente após todos os dados globais foi inicializado e < em> antes do aplicativo de consumo tem a chance de chamar quaisquer funções na DLL. Isto é extremamente útil em alguns casos, e não há absolutamente não comportamento análogo a este em outras plataformas com GCC ou Clang com C ou C ++. O mais próximo que você vai encontrar é fazer uma função de construtor com prioridade (ver acima ponto (2)), o que absolutamente não é a mesma coisa e não vai funcionar para muitos dos casos de uso que DllMain () trabalha.

4) Se você estiver usando CMake para gerar seus sistemas de compilação, que muitas vezes eu fazer, eu descobri que a ordem dos arquivos de origem de entrada será a ordem de seus arquivos de objetos resultantes dadas para o vinculador. No entanto, muitas vezes a sua aplicação / DLL também está ligando em outras bibliotecas, caso em que as bibliotecas estarão na linha de ligação após seus arquivos de origem de entrada. Se você estiver olhando para ter um de seus objetos globais ser o muito primeira para inicializar, então você está na sorte e seu pode colocar o arquivo de origem que contém esse objeto para ser o primeiro na lista de fonte arquivos. No entanto, se você estiver olhando para ter um ser o muito última para inicializar (que pode efetivamente replicar o comportamento DllMain ()!), Então você pode fazer uma chamada para add_library () com que um arquivo de origem para produzir uma biblioteca estática, e adicione a biblioteca estática resultante como a última dependência ligação em seus target_link_libraries () chamada para a sua aplicação / DLL. Seja cauteloso que seu objeto global pode ficar otimizado neste caso e você pode usar o -. flag todo o arquivo para forçar o vinculador não para remover símbolos não utilizados para esse arquivo pequeno específico

Fechando Dica

Para saber com absoluta certeza a ordem de inicialização resultante da sua aplicação /-biblioteca compartilhada ligada, passe --print-mapa para o vinculador ld e grep para .init_array (ou no GCC antes de 4.7, grep para.ctors). Cada construtor mundial serão impressos na ordem em que ele vai ficar inicializado, e lembre-se que a ordem é oposta em GCC antes de 4,7 (ver ponto (1) acima).

O fator de motivação para escrever esta resposta é que eu precisava saber esta informação, não tinha outra escolha a não ser confiar na ordem de inicialização, e encontrou apenas pedaços esparsos de essas informações em toda outros lugares assim e fóruns de internet. A maior parte foi aprendido através de muita experimentação, e espero que isso poupa algumas pessoas o tempo de fazer isso!

http://www.parashift.com/c++ -faq-lite / ctors.html # faq-10.12 - este link se move ao redor. este um é mais estável, mas você terá que olhar ao redor para ele.

edit: osgx fornecido uma melhor link .

Além de comentários de Martin, vindo de um fundo C, eu sempre penso de variáveis ??estáticas, como parte do programa executável, constituída e espaço alocado no segmento de dados. Assim variáveis ??estáticas pode ser pensado como sendo inicializado como o programa é carregado, antes de qualquer código que está sendo executado. A ordem exata em que isso acontece pode ser determinada por olhar para o segmento de dados de saída do arquivo de mapa pelo vinculador, mas para a maioria das intenções e propósitos a inicialização é simultaeneous.

Edit:. Dependendo ordem construção de objetos estáticos é susceptível de ser não-portátil e provavelmente deve ser evitado

Se você realmente quer saber a ordem final eu recomendo que você criar uma classe cujo construtor registra o timestamp atual e criar várias instâncias estáticos da classe em cada um dos seus arquivos CPP para que você possa conhecer a ordem final de inicialização . Certifique-se de colocar um pouco de pouco tempo de operação consumindo no construtor apenas para que você não obter o mesmo carimbo de tempo para cada arquivo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top