¿Está bien usar una variable estática para inicializar / registrarse variables?

StackOverflow https://stackoverflow.com/questions/2337446

  •  22-09-2019
  •  | 
  •  

Pregunta

Idioma: C ++ Toolkit: Qt4

El conjunto de herramientas que estoy usando tiene un método estático llamado int QEvent::registerEventType() registrar mis propios tipos de eventos. Cuando esta subclase QEvent necesito para suministrar la clase de base de este valor. QEvent::QEvent(int type).

¿Está bien usar una variable estática para llamar a esta aplicación antes de que comience? Considere lo siguiente:

//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)
{
}

Como el 'mal' es esto? Podría envolver todo en un espacio de nombres para evitar contaminar el espacio de nombres global.

¿Fue útil?

Solución

inicialización nivel estático es una enorme zona gris compilador-dependiente, como otros han mencionado. Sin embargo, la inicialización del nivel de función no es un área gris y se puede utilizar a su ventaja.

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

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

Esta solución tiene la propiedad de que registerEventType está garantizado para ser llamado antes de tener el tipo de evento, incluso si construye MyEvent durante la inicialización estática, lo cual es bueno, pero lo hace abrir hasta cuestiones de hilo de seguridad si es posible que MyEvent a construirse en varios subprocesos.

Aquí hay una versión flujos seguros, sobre la base de impulso :: 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;
}

Otros consejos

Como C ++ 's inicialización a través de unidades de formación es un área gris grande con mucho margen aplicación, prefiero la chatarra por completo y ser explícito acerca de lo que se hace cuando. (Este rechazo de la orden de inicialización debido a la falta de garantías es similar a cómo clases simples rechazan los objetos globales.) Específicamente, significa esto cualquier estado mundial (variables globales, los miembros de datos estáticos, y la estática de función local) que no puede ser inicializado con constante- expresiones deben inicializarse en exactamente un TU, TU y que es el que implementa principal .

En el caso manual, este medio de inserción y actualización de código en la unidad de traducción que contiene principal y principal en sí. El ejemplo más común de dicho código está llamando srand(time(0)) para sembrar el std :: rand PRNG.

Se puede refactorizar que el manejo manual de código usando el preprocesador:

// 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;
}

Estos archivos .inc son ni cabeceras ni archivos de implementación. La extensión del archivo exacto no importa, siempre y cuando no se utiliza algo que se utiliza comúnmente para encabezados o archivos de implementación, tales como .h, .hpp, .cc, .cpp, y así sucesivamente. Puede generar global_objects.inc y main_init.inc con sede fuera convenciones de nomenclatura de archivos, usando incluyen guardias de modo que las dependencias se pueden incluir (al igual que incluyen guardias de trabajo para los encabezados).

Por ejemplo, ambos de estos archivos se corresponden con myevent.hpp y se colocaría junto a esa cabecera:

// 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

Tenga en cuenta que si sólo se requiere la inicialización de datos estáticos con constantes expresiones, entonces eso es preferible a todas las demás técnicas. La restricción principal para que la inicialización no es ser capaz de hacer una llamada de función (pero en realidad es más compleja), por lo que no se aplica en su caso; este es el único tipo de inicialización variable global que C puede hacer, si desea obtener más información.

Yo uso el "objeto de registro estático" patrón un poco, pero hay que tener en cuenta un gran problema - debe asegurarse de que la cosa se está registrando con, lo que probablemente sí es ser estática, se crea antes de lo que está registrando. Como C ++ no garantiza el orden de construcción estática entre las unidades de traducción, esto puede ser problemático. Una solución es usar el llamado Meyer Singleton:

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

    ... 

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

Como todas las referencias al Registro deben pasar por el método de instancia (), se le garantiza el orden de construcción requerida.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top