Pergunta

Quando eu usar variáveis ??estáticas em C ++, eu muitas vezes acabam por querer inicializar uma variável que passa outro para seu construtor. Em outras palavras, eu quero criar instâncias estáticas que dependem uns dos outros.

Dentro de um único .cpp ou o arquivo .h este não é um problema: as instâncias serão criadas na ordem em que são declaradas. No entanto, quando você quer inicializar uma instância estática com uma instância em outra unidade de compilação, a ordem parece impossível especificar. O resultado é que, dependendo do tempo, pode acontecer que a instância que depende de outro é construída, e só depois a outra instância é construída. O resultado é que a primeira instância é inicializado incorretamente.

Alguém sabe como garantir que objetos estáticos são criados na ordem correta? Tenho pesquisado muito tempo para uma solução, tentando todos eles (incluindo a solução Schwarz Counter), mas eu começar a duvidar há um que realmente funciona.

Uma possibilidade é o truque com o membro função estática:

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

Na verdade, isso não funciona. Infelizmente, você tem que escrever globalObject (). MemberFunction (), em vez de globalObject.MemberFunction (), resultando em um pouco confuso e código de cliente deselegante.

Update: Obrigado por suas reações. Lamentavelmente, na verdade, parece que eu respondi minha própria pergunta. Acho que vou ter que aprender a viver com ela ...

Foi útil?

Solução

Você respondeu à sua própria pergunta. ordem de inicialização estática é indefinido, e da forma mais elegante em torno dele (enquanto ainda está fazendo estática inicialização ou seja, não refatoração-lo desaparecer completamente) é para embrulhar a inicialização em uma função.

Leia os itens C ++ FAQ a partir de https://isocpp.org/wiki / FAQ / ctors # init-static-fim

Outras dicas

Talvez você deveria reconsiderar se você precisa de tantas variáveis ??globais estáticas. Enquanto eles podem às vezes ser útil, muitas vezes, é muito mais simples de refatorar-los para um âmbito local menor, especialmente se você descobrir que algumas variáveis ??estáticas depender dos outros.

Mas você está certo, não há nenhuma maneira de garantir uma ordem particular de inicialização, e por isso, se o seu coração está definido sobre ele, mantendo a inicialização em uma função, como você mencionou, é provavelmente a maneira mais simples.

Na verdade, isso não funciona. Infelizmente, você tem que escrever globalObject (). MemberFunction (), em vez de globalObject.MemberFunction (), resultando em um pouco confuso e código de cliente deselegante.

Mas a coisa mais importante é que ele funciona, e que é à prova de falhas, ie. não é fácil para ignorar o uso correto.

correção de programas deve ser sua primeira prioridade. Além disso, IMHO, o () acima é puramente estilística - isto é. completamente sem importância.

Dependendo da sua plataforma, tenha cuidado com demasiada inicialização dinâmica. Há uma quantidade relativamente pequena de até limpa que pode ocorrer para initializers dinâmicas (consulte aqui ). Você pode resolver este problema utilizando um recipiente de objeto global que contém membros diferentes objetos globais. Portanto, você tem:

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

Há apenas uma chamada para Globals ~ (), a fim de limpar para todos os objetos globais em seu programa. Para acessar a global você ainda tem algo como:

getGlobals().configuration.memberFunction ();

Se você realmente queria que você poderia envolver isso em uma macro para salvar um pouco de digitação usando um macro:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

Embora, este é o açúcar apenas sintática na sua solução inicial.

A maioria dos compiladores (ligantes) realmente fazem suportar uma forma (não-portátil) de especificar a ordem. Por exemplo, com visual studio você pode usar o init_seg pragma para organizar a inicialização em vários grupos diferentes. AFAIK não há nenhuma maneira de fim garantia dentro de cada grupo. Uma vez que este é não-portátil que você pode querer considerar se você pode corrigir seu projeto para não exigir isso, mas a opção está lá fora.

dispite a idade de esta discussão, eu gostaria de propor a solução que eu encontrei. Como muitos têm apontado antes de mim, C ++ não fornece qualquer mecanismo de ordenação de inicialização estática. O que eu proponho é encapsule cada membro estático dentro de um método estático da classe que, por sua vez inicializar o membro e fornecer um acesso de forma orientada a objetos. Deixe-me dar um exemplo, supondo que queremos para definir a classe com o nome "Matemática", que, entre os outros membros, contém "PI":

class Math {
public:
   static const float Pi() {
       static const float s_PI = 3.14f;
       return s_PI;
   }
}

s_PI será inicializado pela primeira vez Pi () método é invocado (em GCC). Esteja ciente: os objetos locais com armazenamento estático têm uma lifecyle dependente da implementação, para posterior verificação detalhe 6.7.4 em 2 .

estático palavra-chave , C ++ padrão

Embalagem da estática em um método vai resolver o problema fim, mas não é thread-safe como outros apontaram, mas você pode fazer isso também torná-lo enfiar se isso é uma preocupação.

// File scope static pointer is thread safe and is initialized first.
static Type * theOneAndOnlyInstance = 0;

Type& globalObject()
{
    if(theOneAndOnlyInstance == 0)
    {
         // Put mutex lock here for thread safety
         theOneAndOnlyInstance = new Type();
    }

    return *theOneAndOnlyInstance;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top