¿Cuál es la mejor manera de inicializar un miembro de datos estático privado en C ++? Intenté esto en mi archivo de encabezado, pero me da errores raros de enlazador:
class foo
{
private:
static int i;
};
int foo::i = 0;
Supongo que esto se debe a que no puedo inicializar un miembro privado desde fuera de la clase. Entonces, ¿cuál es la mejor manera de hacer esto?
c++
initialization
static-members
Jason Baker
fuente
fuente
inline static int x[] = {1, 2, 3};
. Ver en.cppreference.com/w/cpp/language/static#Static_data_membersRespuestas:
La declaración de clase debe estar en el archivo de encabezado (o en el archivo de origen si no se comparte).
Archivo: foo.h
Pero la inicialización debe estar en el archivo fuente.
Archivo: foo.cpp
Si la inicialización está en el archivo de encabezado, entonces cada archivo que incluye el archivo de encabezado tendrá una definición del miembro estático. Por lo tanto, durante la fase de enlace, obtendrá errores de enlace ya que el código para inicializar la variable se definirá en múltiples archivos de origen. La inicialización de la
static int i
debe hacerse fuera de cualquier función.Nota: Matt Curtis: señala que C ++ permite la simplificación de lo anterior si la variable miembro estática es de tipo int constante (p
int
. Ej .bool
,char
). Luego puede declarar e inicializar la variable miembro directamente dentro de la declaración de clase en el archivo de encabezado:fuente
Para una variable :
foo.h:
foo.cpp:
Esto se debe a que solo puede haber una instancia
foo::i
en su programa. Es una especie de equivalenteextern int i
en un archivo de encabezado yint i
en un archivo fuente.Para una constante , puede poner el valor directamente en la declaración de clase:
fuente
private
variables se pueden inicializar fuera de la clase aquí, ¿se puede hacer esto también para variables no estáticas?Class
no tiene ningún sentido en Cpp.Desde C ++ 17, los miembros estáticos pueden definirse en el encabezado con la palabra clave en línea .
http://en.cppreference.com/w/cpp/language/static
"Un miembro de datos estáticos puede declararse en línea. Un miembro de datos estáticos en línea puede definirse en la definición de clase y puede especificar un inicializador de miembro predeterminado. No necesita una definición fuera de clase:"
fuente
Para los futuros espectadores de esta pregunta, quiero señalar que deben evitar lo que monkey0506 sugiere .
Los archivos de encabezado son para declaraciones.
Los archivos de encabezado se compilan una vez por cada
.cpp
archivo que los directa o indirectamente#includes
, y el código fuera de cualquier función se ejecuta en la inicialización del programa, antesmain()
.Al poner:
foo::i = VALUE;
en el encabezado,foo:i
se le asignará el valorVALUE
(lo que sea que sea) para cada.cpp
archivo, y estas asignaciones sucederán en un orden indeterminado (determinado por el vinculador) antes demain()
ejecutarse.¿Qué pasa si somos
#define VALUE
un número diferente en uno de nuestros.cpp
archivos? Se compilará bien y no tendremos forma de saber cuál gana hasta que ejecutemos el programa.Nunca coloque el código ejecutado en un encabezado por la misma razón que nunca
#include
un.cpp
archivo.incluir guardias (que estoy de acuerdo en que siempre debe usar) lo protegen de algo diferente: el mismo encabezado se usa indirectamente
#include
varias veces al compilar un solo.cpp
archivofuente
Con un compilador de Microsoft [1], las variables estáticas que no son
int
similares también se pueden definir en un archivo de encabezado, pero fuera de la declaración de clase, utilizando el específico de Microsoft__declspec(selectany)
.Tenga en cuenta que no digo que esto sea bueno, solo digo que se puede hacer.
[1] En estos días, hay más compiladores que el soporte de MSC
__declspec(selectany)
, al menos gcc y clang. Quizás aún más.fuente
Es la sintaxis correcta para inicializar la variable, pero debe ir en el archivo fuente (.cpp) en lugar de en el encabezado.
Debido a que es una variable estática, el compilador necesita crear solo una copia de ella. Debe tener una línea "int foo: i" en algún lugar de su código para indicarle al compilador dónde colocarlo; de lo contrario, obtendrá un error de enlace. Si está en un encabezado, obtendrá una copia en cada archivo que incluya el encabezado, por lo tanto, obtenga múltiples errores de símbolos definidos desde el enlazador.
fuente
No tengo suficiente representante aquí para agregar esto como un comentario, pero en mi opinión, es un buen estilo escribir sus encabezados con #include guardias de todos modos, lo que, como señaló Paranaix hace unas horas, evitaría un error de definición múltiple. A menos que ya esté usando un archivo CPP separado, no es necesario usar uno solo para inicializar miembros no integrales estáticos.
No veo la necesidad de usar un archivo CPP separado para esto. Claro que puedes, pero no hay una razón técnica por la que debas hacerlo.
fuente
#endif // FOO_H
#pragma once
Si desea inicializar algún tipo compuesto (cadena fe), puede hacer algo así:
Como
ListInitializationGuard
es unSomeClass::getList()
método interno de variable estática , se construirá solo una vez, lo que significa que se llama al constructor una vez. Esto variaráinitialize _list
al valor que necesita. Cualquier llamada posterior agetList
simplemente devolverá el_list
objeto ya inicializado .Por supuesto, debe acceder al
_list
objeto siempre llamando algetList()
método.fuente
Patrón de constructor estático C ++ 11 que funciona para múltiples objetos
Se propuso un modismo en: https://stackoverflow.com/a/27088552/895245, pero aquí hay una versión más limpia que no requiere la creación de un nuevo método por miembro.
main.cpp
GitHub aguas arriba .
Compilar y ejecutar:
Ver también: constructores estáticos en C ++? Necesito inicializar objetos privados estáticos
Probado en Ubuntu 19.04.
C ++ 17 variable en línea
Mencionado en: https://stackoverflow.com/a/45062055/895245 pero aquí hay un ejemplo ejecutable de múltiples archivos para hacerlo aún más claro: ¿Cómo funcionan las variables en línea?
fuente
También puede incluir la asignación en el archivo de encabezado si usa protectores de encabezado. He usado esta técnica para una biblioteca C ++ que he creado. Otra forma de lograr el mismo resultado es usar métodos estáticos. Por ejemplo...
El código anterior tiene la "ventaja" de no requerir un archivo CPP / fuente. De nuevo, un método que uso para mis bibliotecas C ++.
fuente
Sigo la idea de Karl. Me gusta y ahora también lo uso. He cambiado un poco la notación y agregué algunas funcionalidades
esto produce
fuente
También trabajando en el archivo privateStatic.cpp:
fuente
¿Qué hay de un
set_default()
método?Solo tendríamos que usar el
set_default(int x)
método y nuestrastatic
variable se inicializaría.Esto no estaría en desacuerdo con el resto de los comentarios, en realidad sigue el mismo principio de inicializar la variable en un ámbito global, pero al usar este método lo hacemos explícito (y fácil de ver y entender) en lugar de tener la definición de la variable colgando allí.
fuente
El problema del enlazador que encontró probablemente sea causado por:
Este es un problema común para aquellos que comienzan con C ++. El miembro de clase estático debe inicializarse en una sola unidad de traducción, es decir, en un único archivo fuente.
Desafortunadamente, el miembro de la clase estática debe inicializarse fuera del cuerpo de la clase. Esto complica escribir código de solo encabezado y, por lo tanto, estoy usando un enfoque bastante diferente. Puede proporcionar su objeto estático a través de una función de clase estática o no estática, por ejemplo:
fuente
Una forma "antigua" de definir constantes es reemplazarlas por
enum
:De esta manera, no es necesario proporcionar una definición y evita hacer el valor constante l , lo que puede ahorrarle algunos dolores de cabeza, por ejemplo, cuando lo usa ODR accidentalmente .
fuente
Solo quería mencionar algo un poco extraño para mí cuando me encontré con esto por primera vez.
Necesitaba inicializar un miembro privado de datos estáticos en una clase de plantilla.
en .h o .hpp, se ve algo así para inicializar un miembro de datos estáticos de una clase de plantilla:
fuente
¿Esto sirve a su propósito?
fuente