Pergunta

Linguagem:Kit de ferramentas C ++:Qt4

O kit de ferramentas que estou usando possui um método estático chamado int QEvent::registerEventType() para registrar meus próprios tipos de eventos.Quando eu subclassificar isso QEvent Preciso fornecer esse valor à classe base. QEvent::QEvent(int type).

Posso usar uma variável estática para chamar isso antes do início do aplicativo?Considere o seguinte:

//This is all in my .cpp file

static int myEventType;  //This will contain my registered type

/*If I create a static instance of this class the constructor 
  gets called before the main() function starts.
*/
class DoRegisterMyEventType {  
public:
  DoRegisterMyEventType() {
    myEventType = QEvent::registerEventType();
  }
};

static DoRegisterMyEventType doRegisterMyEventType;

//Here is the constructor for MyEvent class which inherits QEvent.
MyEvent::MyEvent()
  : QEvent(myEventType)
{
}

Quão 'maligno' é isso?Eu poderia agrupar tudo em um namespace para evitar poluir o namespace global.

Foi útil?

Solução

A inicialização do nível estático é uma enorme área cinzenta dependente do compilador, como outros mencionaram. No entanto, a inicialização do nível da função não é uma área cinza e pode ser usada para sua vantagem.

static inline int GetMyEventType()
{
    static int sEventType = QEvent::registerEventType();
    return sEventType;
}

MyEvent::MyEvent()
  : QEvent(GetMyEventType())
{
}

Esta solução possui a propriedade que o RegisterEventType é garantido para ser chamado antes de você precisar do seu tipo de evento, mesmo se você construir myevent durante a inicialização estática, o que é bom, mas o abre para problemas de segurança de tópicos, se for possível para que o myevent seja construído em vários tópicos.

Aqui está uma versão segura para threads, baseada no boost :: call_once:

#include "boost/thread/once.hpp"

static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.    
static int sEventType = -1; //-1 is not a valid event

static void DoRegister()
{
    sEventType = QEvent::registerEventType();
}

static inline int GetMyEventType()
{
    boost::call_once(sHaveRegistered, &DoRegister);
    return sEventType;
}

Outras dicas

Desde a inicialização do C++ em todas as TUs é uma grande área cinzenta com muita margem de implementação, prefiro descartá-la completamente e ser explícito sobre o que será feito e quando.(Esta rejeição da ordem de inicialização devido à falta de garantias é semelhante à forma como aulas únicas rejeitar objetos globais.) Especificamente, isso significa que qualquer estado global (variáveis ​​globais, membros de dados estáticos e estática local de função) que não pode ser inicializado com expressões constantes deve ser inicializado em exatamente uma TU, e essa TU é aquela que implementa principal.

No caso manual, isso significa inserir e atualizar o código na unidade de tradução que contém principal e em principal em si.O exemplo mais comum desse código é chamar srand(time(0)) para semear o std::rand PRNG.

Você pode refatorar esse gerenciamento manual de código usando o pré-processador:

// the implementation file for main, could be named main.cpp

#include "whatever_declares_the_real_main.hpp"

#include "global_objects.inc"

int main(int argc, char* argv[]) try {
#include "main_init.inc"

  return the_real_main(argc, argv);

  // main.cpp has well-defined responsibility:
  // initialize global state before passing control to another function, and
  // handle return-code or exceptions

  // you can modify this, depending on your preference and desired API
  // for example:
  return the_real_main(std::vector<std::string>(argv+1, argv+argc));
  return the_real_main(parse_args(argv+1, argv+argc));
  // just make sure to keep main.cpp's responsibility well-defined and
  // relatively simple
}
// example handling; depending on your specifics, you might do something
// different, or know how to provide more information:
catch (std::exception& e) {
  std::cerr << "abnormal termination: " << e.what() << '\n';
  return 1;
}
catch (...) {
  std::cerr << "abnormal termination.\n";
  return 1;
}

Esses arquivos .inc não são cabeçalhos nem arquivos de implementação.A extensão exata do arquivo não importa, desde que você não use algo que seja comumente usado para cabeçalhos ou arquivos de implementação, como .h, .hpp, .cc, .cpp e assim por diante.Você pode gerar global_objects.inc e main_init.inc baseado em convenções de nomenclatura de arquivos, usando protetores de inclusão para que dependências possam ser incluídas (assim como os protetores de inclusão funcionam para cabeçalhos).

Por exemplo, ambos os arquivos correspondem a meuevento.hpp e seria colocado ao lado desse cabeçalho:

// file "myevent.global_inc"
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868

#include <QEvent.hpp> // or whatever headers you need

#include "myevent.hpp" // declares the variable defined just below
// (remember you use 'extern' to declare objects without defining them)

int your_namespace::myEventType = QEvent::registerEventType();

#endif

// file "myevent.main_inc"
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81

// nothing needed in this case, from what you've shown so far

// this is where you place expressions that would otherwise require a dummy
// global variable to make sure they are executed, but this also allows use
// of temporary variables while includes handle dependency order:
#include "something_else.main_inc" // fake example dependency, which must
{                                  // be executed first
  int temp;
  some_func(&temp);
  other_func(temp); // not easy to transform this into a global's init
  // expression, yet defining it this way is natural, because it's exactly
  // how you would do it inside a function
}

#endif

Observe que se você precisar apenas de inicialização de dados estáticos com expressões constantes, isso será preferível a todas as outras técnicas.A principal restrição para essa inicialização é não poder fazer uma chamada de função (mas na verdade é mais complexa), portanto não se aplica ao seu caso;este é o único tipo de inicialização de variável global que C pode fazer, se você quiser saber mais.

Eu uso o padrão de "objeto de registro estático" um pouco, mas você deve estar ciente de um grande problema - você deve garantir que o que está se registrando, que provavelmente será estático, é criado antes do que você está registrando . Como o C ++ não garante a ordem da construção estática entre as unidades de tradução, isso pode ser problemático. Uma solução é usar o chamado Meyer Singleton:

class Registry {
  public:
    static Registry & Instance() {
        static Registry r;
        return r;
    }

    ... 

 private:
    Registry() {    
      ...
    }
};

Como todas as referências ao registro devem passar pelo método da instância (), você tem a ordem de construção necessária.

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