Quiero tener una clase con un miembro privado de datos estáticos (un vector que contiene todos los caracteres az). En java o C #, simplemente puedo hacer un "constructor estático" que se ejecutará antes de hacer cualquier instancia de la clase, y configurar los miembros de datos estáticos de la clase. Solo se ejecuta una vez (ya que las variables son de solo lectura y solo necesitan configurarse una vez) y dado que es una función de la clase, puede acceder a sus miembros privados. Podría agregar código en el constructor que verifica si el vector se inicializa e inicializarlo si no es así, pero eso introduce muchas verificaciones necesarias y no parece ser la solución óptima para el problema.
Se me ocurre que, dado que las variables serán de solo lectura, solo pueden ser constantes estáticas públicas, por lo que puedo configurarlas una vez fuera de la clase, pero una vez más, parece una especie de truco feo.
¿Es posible tener miembros privados de datos estáticos en una clase si no quiero inicializarlos en el constructor de instancias?
fuente
Respuestas:
Para obtener el equivalente de un constructor estático, debe escribir una clase ordinaria separada para contener los datos estáticos y luego hacer una instancia estática de esa clase ordinaria.
fuente
friend
tiene mucho sentido para que la claseElsewhere
pueda acceder fácilmente aStaticStuff
los elementos internos (sin interrumpir la encapsulación de ninguna manera peligrosa, podría agregar).Bueno puedes tener
No olvides (en el .cpp) esto:
El programa aún se vinculará sin la segunda línea, pero el inicializador no se ejecutará.
fuente
MyClass::a.push_back(i)
lugar dea.push_back(i)
?_initializer
es un subobjeto deMyClass
. Los subobjetos se inicializan en este orden: subobjetos de clase base virtual, en orden de profundidad primero, de izquierda a derecha (pero solo inicializando cada subobjeto distinto una vez); luego subobjetos de clase base, en profundidad primero, de izquierda a derecha; luego los subobjetos de miembros en orden de declaración. Por lo tanto, es seguro usar la estrategia de EFraim, siempre que el código_initialiser
solo se refiera a los miembros declarados antes.Solución C ++ 11
Desde C ++ 11, simplemente puede usar expresiones lambda para inicializar miembros de clase estáticos. Esto incluso funciona si necesita imponer un orden de construcción entre los distintos miembros estáticos, o si tiene miembros estáticos que son
const
.Archivo de cabecera:
Archivo fuente:
fuente
try catch
bloque si se pueden generar excepciones.En el archivo .h:
En el archivo .cpp:
fuente
Aquí hay otro enfoque similar al de Daniel Earwicker, que también utiliza la sugerencia de clase de amigo de Konrad Rudolph. Aquí usamos una clase de utilidad de amigo privado interno para inicializar los miembros estáticos de su clase principal. Por ejemplo:
Archivo de cabecera:
Archivo de implementación:
Este enfoque tiene la ventaja de ocultar completamente la clase Initializer del mundo exterior, manteniendo todo lo contenido dentro de la clase para inicializar.
fuente
ToBeInitialized::Initializer::Initializer()
se llama, por lo que debe agregarToBeInitialized::Initializer ToBeInitialized::initializer;
al archivo de implementación. Tomé algunas cosas de su idea y de la idea de EFraim, y funciona exactamente como lo necesito y se ve limpio. Gracias hombre.Test::StaticTest()
se llama exactamente una vez durante la inicialización estática global.La persona que llama solo tiene que agregar una línea a la función que será su constructor estático.
static_constructor<&Test::StaticTest>::c;
fuerza la inicialización dec
durante la inicialización estática global.fuente
Sin necesidad de una
init()
función,std::vector
se puede crear desde un rango:Sin embargo, tenga en cuenta que las estadísticas de tipo de clase causan problemas en las bibliotecas, por lo que deben evitarse allí.
Actualización de C ++ 11
A partir de C ++ 11, puede hacer esto en su lugar:
Es semánticamente equivalente a la solución C ++ 98 en la respuesta original, pero no puede usar un literal de cadena en el lado derecho, por lo que no es completamente superior. Sin embargo, si tiene un vector de cualquier otro tipo que
char
,wchar_t
,char16_t
ochar32_t
(arrays de los cuales se puede escribir como literales de cadena), la versión C ++ 11 eliminará estrictamente código repetitivo sin introducir otra sintaxis, en comparación con el C ++ 98 versión.fuente
El concepto de constructores estáticos se introdujo en Java después de que aprendieron de los problemas en C ++. Entonces no tenemos un equivalente directo.
La mejor solución es usar tipos de POD que puedan inicializarse explícitamente.
O haga que sus miembros estáticos sean de un tipo específico que tenga su propio constructor que lo inicialice correctamente.
fuente
Cuando intento compilar y usar class
Elsewhere
(de la respuesta de Earwicker ) obtengo:Parece que no es posible inicializar atributos estáticos de tipos no enteros sin poner algún código fuera de la definición de clase (CPP).
Para hacer esa compilación, puede usar " un método estático con una variable local estática dentro ". Algo como esto:
Y también puede pasar argumentos al constructor o inicializarlo con valores específicos, es muy flexible, potente y fácil de implementar ... lo único es que tiene un método estático que contiene una variable estática, no un atributo estático ... La sintaxis cambia un poco, pero sigue siendo útil. Espero que esto sea útil para alguien,
Hugo González Castro.
fuente
Supongo que la solución simple a esto será:
fuente
Acabo de resolver el mismo truco. Tuve que especificar la definición de un solo miembro estático para Singleton. Pero haga las cosas más complicadas: he decidido que no quiero llamar al ctor de RandClass () a menos que lo use ... es por eso que no quería inicializar singleton globalmente en mi código. También he agregado una interfaz simple en mi caso.
Aquí está el código final:
Simplifiqué el código y utilicé la función rand () y su inicializador de inicio único srand ()
fuente
Aquí está mi variante de la solución de EFraim; la diferencia es que, gracias a la creación de instancias implícita de plantilla, el constructor estático solo se llama si se crean instancias de la clase, y que no
.cpp
se necesita ninguna definición en el archivo (gracias a la magia de creación de instancias de plantilla).En el
.h
archivo, tienes:En el
.cpp
archivo, puede tener:Tenga en cuenta que
MyClass::a
solo se inicializa si la línea [1] está allí, porque eso llama (y requiere la creación de instancias) del constructor, que luego requiere la creación de instancias de_initializer
.fuente
Aquí hay otro método, donde el vector es privado para el archivo que contiene la implementación mediante el uso de un espacio de nombres anónimo. Es útil para cosas como tablas de búsqueda que son privadas para la implementación:
fuente
I
yi
algo un poco más oscuro para que no los use accidentalmente en algún lugar más bajo del archivo.Ciertamente no necesita ser tan complicado como la respuesta actualmente aceptada (por Daniel Earwicker). La clase es superflua. No hay necesidad de una guerra de idiomas en este caso.
archivo .hpp:
archivo .cpp:
fuente
Ofertas de GCC
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
Etiquete un método estático con este atributo y se ejecutará en la carga del módulo, antes de main ().
fuente
Defina variables miembro estáticas de forma similar a la forma en que define los métodos miembros.
foo.h
foo.cpp
fuente
Para inicializar una variable estática, solo debe hacerlo dentro de un archivo fuente. Por ejemplo:
fuente
¿Qué tal crear una plantilla para imitar el comportamiento de C #?
fuente
Para casos simples como aquí, una variable estática envuelta dentro de una función miembro estática es casi tan buena. Es simple y generalmente será compilado por los compiladores. Sin embargo, esto no resuelve el problema del orden de inicialización para objetos complejos.
fuente
¿Es esta una solución?
fuente
Se puede emular un constructor estático utilizando una clase amiga o una clase anidada como se muestra a continuación.
Salida:
fuente
new
utilizando una matriz de caracteres solo para filtrar inmediatamente el puntero y sobrescribirlo?Wow, no puedo creer que nadie mencionó la respuesta más obvia, y una que imita más estrechamente el comportamiento del constructor estático de C #, es decir, no se llama hasta que se crea el primer objeto de ese tipo.
std::call_once()
está disponible en C ++ 11; si no puede usar eso, se puede hacer con una variable de clase booleana estática y una operación atómica de comparar e intercambiar. En su constructor, vea si puede cambiar atómicamente el indicador de clase estática defalse
atrue
, y si es así, puede ejecutar el código de construcción estática.Para obtener crédito adicional, conviértalo en un indicador de 3 vías en lugar de un valor booleano, es decir, no ejecutar, ejecutar y finalizar la ejecución. Luego, todas las demás instancias de esa clase pueden girar y bloquear hasta que la instancia que ejecuta el constructor estático haya finalizado (es decir, emita un límite de memoria, luego establezca el estado en "ejecución finalizada"). Su spin-lock debe ejecutar la instrucción de "pausa" del procesador, duplicar la espera cada vez hasta un umbral, etc. - técnica de bloqueo de spin bastante estándar.
En ausencia de C ++ 11, esto debería ayudarlo a comenzar.
Aquí hay un pseudocódigo para guiarte. Pon esto en la definición de tu clase:
Y esto en tu constructor:
fuente