Pregunta

Cuando uso variables estáticas en C ++, a menudo termino queriendo inicializar una variable pasando otra a su constructor. En otras palabras, quiero crear instancias estáticas que dependan unas de otras.

Dentro de un único archivo .cpp o .h, esto no es un problema: las instancias se crearán en el orden en que se declararon. Sin embargo, cuando desea inicializar una instancia estática con una instancia en otra unidad de compilación, el orden parece imposible de especificar. El resultado es que, dependiendo del clima, puede ocurrir que la instancia que depende de otra se construya, y solo después se construya la otra instancia. El resultado es que la primera instancia se inicializa incorrectamente.

¿Alguien sabe cómo asegurarse de que los objetos estáticos se creen en el orden correcto? He buscado durante mucho tiempo una solución, probando todas ellas (incluida la solución Schwarz Counter), pero empiezo a dudar de que haya una que realmente funcione.

Una posibilidad es el truco con el miembro de la función estática:

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

De hecho, esto funciona. Lamentablemente, tiene que escribir globalObject (). MemberFunction (), en lugar de globalObject.MemberFunction (), lo que resulta en un código de cliente poco confuso y poco elegante.

Actualización: Gracias por sus reacciones. Lamentablemente, de hecho parece que he respondido a mi propia pregunta. Supongo que tendré que aprender a vivir con eso ...

¿Fue útil?

Solución

Has respondido tu propia pregunta. El orden de inicialización estática no está definido, y la forma más elegante de evitarlo (mientras se realiza la inicialización estática, es decir, no se está refactorizando completamente) es envolver la inicialización en una función.

Lea los elementos de Preguntas frecuentes de C ++ que comienzan en https://isocpp.org/wiki / faq / ctors # static-init-order

Otros consejos

Tal vez debería reconsiderar si necesita tantas variables estáticas globales. Aunque algunas veces pueden ser útiles, a menudo es mucho más sencillo refactorizarlas a un alcance local más pequeño, especialmente si encuentra que algunas variables estáticas dependen de otras.

Pero tienes razón, no hay manera de asegurar un orden particular de inicialización, por lo que si tu corazón está puesto en él, mantener la inicialización en una función, como mencionaste, es probablemente la forma más sencilla.

  

De hecho, esto funciona. Lamentablemente, tiene que escribir globalObject (). MemberFunction (), en lugar de globalObject.MemberFunction (), lo que resulta en un código de cliente poco confuso y poco elegante.

Pero lo más importante es que funciona, y que está a prueba de fallos, es decir. no es fácil pasar por alto el uso correcto.

La corrección del programa debe ser su primera prioridad. También, en mi humilde opinión, el () anterior es puramente estilístico, es decir. completamente sin importancia.

Dependiendo de su plataforma, tenga cuidado con demasiada inicialización dinámica. Hay una cantidad relativamente pequeña de limpieza que se puede llevar a cabo para los inicializadores dinámicos (consulte aquí ). Puede resolver este problema utilizando un contenedor de objetos global que contiene miembros objetos globales diferentes. Por lo tanto tienes:

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

Solo hay una llamada a ~ Globals () para limpiar todos los objetos globales en tu programa. Para acceder a un global todavía tienes algo como:

getGlobals().configuration.memberFunction ();

Si realmente quisiera, podría envolver esto en una macro para ahorrar un poco de escritura usando una macro:

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

Aunque, esto es solo azúcar sintáctica en su solución inicial.

La mayoría de los compiladores (enlazadores) realmente admiten una forma (no portátil) de especificar el pedido. Por ejemplo, con Visual Studio puede usar init_seg pragma para organizar la inicialización en varios grupos diferentes. AFAIK no hay forma de garantizar el orden DENTRO de cada grupo. Dado que esto no es portátil, es posible que desee considerar si puede arreglar su diseño para que no lo requiera, pero la opción está ahí.

A pesar de la edad de este hilo, me gustaría proponer la solución que encontré. Como muchos han señalado anteriormente, C ++ no proporciona ningún mecanismo para la ordenación de inicialización estática. Lo que propongo es encapsular cada miembro estático dentro de un método estático de la clase que a su vez inicializa el miembro y proporciona un acceso orientado a objetos. Permítame darle un ejemplo, suponiendo que queremos definir la clase llamada "Matemáticas". que, entre los otros miembros, contiene " PI " ;:

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

s_PI se inicializará la primera vez que se invoque el método Pi () (en GCC). Tenga en cuenta: los objetos locales con almacenamiento estático tienen un ciclo de vida dependiente de la implementación, para una verificación más detallada 6.7.4 en 2 .

Palabra clave estática , C ++ Standard

Ajustar la estática en un método solucionará el problema de la orden, pero no es seguro para los subprocesos, como otros lo han señalado, pero también puedes hacer esto para que sea un subproceso si es una preocupación.

// 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top