objetos miembros estaticos estaticas c++ initialization static-members

estaticas - miembros estaticos c++



¿Cómo inicializar miembros estáticos privados en C++? (17)

¿Esto sirve para tu propósito?

//header file struct MyStruct { public: const std::unordered_map<std::string, uint32_t> str_to_int{ { "a", 1 }, { "b", 2 }, ... { "z", 26 } }; const std::unordered_map<int , std::string> int_to_str{ { 1, "a" }, { 2, "b" }, ... { 26, "z" } }; std::string some_string = "justanotherstring"; uint32_t some_int = 42; static MyStruct & Singleton() { static MyStruct instance; return instance; } private: MyStruct() {}; }; //Usage in cpp file int main(){ std::cout<<MyStruct::Singleton().some_string<<std::endl; std::cout<<MyStruct::Singleton().some_int<<std::endl; return 0; }

¿Cuál es la mejor manera de inicializar un miembro de datos estáticos privados en C ++? Intenté esto en mi archivo de encabezado, pero me da errores extraños del enlazador:

class foo { private: static int i; }; int foo::i = 0;

Supongo que esto se debe a que no puedo inicializar a un miembro privado fuera de la clase. Entonces, ¿cuál es la mejor manera de hacer esto?


¿Qué pasa con un método set_default() ?

class foo { public: static void set_default(int); private: static int i; }; void foo::set_default(int x) { i = x; }

Solo tendríamos que usar el set_default(int x) y nuestra variable static 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 que cuelga allí.


Con un compilador de Microsoft [1], las variables estáticas que no son similares a int también se pueden definir en un archivo de encabezado, pero fuera de la declaración de clase, utilizando la __declspec(selectany) específica de Microsoft __declspec(selectany) .

class A { static B b; } __declspec(selectany) A::b;

Tenga en cuenta que no estoy diciendo que esto sea bueno, solo digo que se puede hacer.

[1] En estos días, más compiladores que MSC admiten __declspec(selectany) - al menos gcc y clang. Tal vez incluso más.


Desde C ++ 17, los miembros estáticos se pueden definir 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 ser declarado en línea. Un miembro de datos estáticos en línea se puede definir en la definición de clase y puede especificar un inicializador de miembro predeterminado. No necesita una definición fuera de clase:"

struct X { inline static int n = 1; };


El problema del enlazador que encontraste es probablemente causado por:

  • Proporcionar tanto la clase como la definición de miembro estática en el archivo de cabecera,
  • Incluyendo este encabezado en dos o más archivos de origen.

Este es un problema común para aquellos que comienzan con C ++. El miembro de la clase estática debe inicializarse en una sola unidad de traducción, es decir, en un solo archivo fuente.

Desafortunadamente, el miembro de la clase estática debe inicializarse fuera del cuerpo de la clase. Esto complica la escritura de código de solo encabezado y, por lo tanto, estoy usando un enfoque muy diferente. Puede proporcionar su objeto estático a través de la función de clase estática o no estática, por ejemplo:

class Foo { // int& getObjectInstance() const { static int& getObjectInstance() { static int object; return object; } void func() { int &object = getValueInstance(); object += 5; } };


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

class foo { private: static int i; };

Pero la inicialización debe estar en el archivo fuente.
Archivo: foo.cpp

int foo::i = 0;

Si la inicialización está en el archivo de encabezado, cada archivo que incluya el archivo de encabezado tendrá una definición del miembro estático. Por lo tanto, durante la fase de enlace obtendrá errores de vinculador, ya que el código para inicializar la variable se definirá en varios archivos de origen.

Nota: Matt Curtis: señala que C ++ permite la simplificación de lo anterior si la variable miembro estática es de tipo int int (por ejemplo, int , bool , char ). Luego puede declarar e inicializar la variable miembro directamente dentro de la declaración de clase en el archivo de encabezado:

class foo { private: static int const i = 42; };


No tengo suficientes representantes aquí para agregar esto como un comentario, pero en mi opinión, es un buen estilo para 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é utilizando un archivo CPP separado, no es necesario usar uno solo para inicializar los miembros estáticos no integrales.

#ifndef FOO_H #define FOO_H #include "bar.h" class foo { private: static bar i; }; bar foo::i = VALUE; #endif

No veo la necesidad de usar un archivo CPP separado para esto. Claro que puedes, pero no hay ninguna razón técnica por la que debas hacerlo.


Para futuros espectadores de esta pregunta, quiero señalar que deben evitar lo que sugiere monkey0506 .

Los archivos de encabezado son para declaraciones.

Los archivos de encabezado se compilan una vez por cada archivo .cpp que directa o indirectamente #includes los .cpp , y el código fuera de cualquier función se ejecuta en la inicialización del programa, antes de main() .

Poniendo: foo::i = VALUE; en el encabezado, foo:i asignará el valor VALUE (cualquiera que sea) para cada archivo .cpp , y estas asignaciones sucederán en un orden indeterminado (determinado por el vinculador) antes de ejecutar main() .

¿Qué .cpp si #define VALUE para ser un número diferente en uno de nuestros archivos .cpp ? 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 el mismo motivo por el que nunca .cpp un archivo .cpp .

incluir guardias (que estoy de acuerdo con que siempre deberías usar) te protege de algo diferente: el mismo encabezado es indirectamente #include d varias veces al compilar un solo archivo .cpp


Para una variable :

foo.h:

class foo { private: static int i; };

foo.cpp:

int foo::i = 0;

Esto se debe a que solo puede haber una instancia de foo::i en su programa. Es una especie de equivalente a extern int i en un archivo de encabezado e int i en un archivo fuente.

Para una constante puede poner el valor directamente en la declaración de clase:

class foo { private: static int i; const static int a = 42; };


Si quieres inicializar algún tipo compuesto (cadena de fe) puedes hacer algo como eso:

class SomeClass { static std::list<string> _list; public: static const std::list<string>& getList() { struct Initializer { Initializer() { // Here you may want to put mutex _list.push_back("FIRST"); _list.push_back("SECOND"); .... } } static Initializer ListInitializationGuard; return _list; } };

Como ListInitializationGuard es una variable estática dentro del SomeClass::getList() , se construirá solo una vez, lo que significa que se llama al constructor una vez. Esto initialize _list variable initialize _list al valor que necesita. Cualquier llamada posterior a getList simplemente devolverá el objeto _list ya inicializado.

Por supuesto, siempre debe acceder _list objeto getList() llamando getList() método getList() .


Sigo la idea de Karl. Me gusta y ahora lo uso también. He cambiado un poco la notación y he añadido algunas funcionalidades.

#include <stdio.h> class Foo { public: int GetMyStaticValue () const { return MyStatic(); } int & GetMyStaticVar () { return MyStatic(); } static bool isMyStatic (int & num) { return & num == & MyStatic(); } private: static int & MyStatic () { static int mStatic = 7; return mStatic; } }; int main (int, char **) { Foo obj; printf ("mystatic value %d/n", obj.GetMyStaticValue()); obj.GetMyStaticVar () = 3; printf ("mystatic value %d/n", obj.GetMyStaticValue()); int valMyS = obj.GetMyStaticVar (); int & iPtr1 = obj.GetMyStaticVar (); int & iPtr2 = valMyS; printf ("is my static %d %d/n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2)); }

esto produce

mystatic value 7 mystatic value 3 is my static 1 0


Solo quería mencionar algo un poco extraño para mí cuando me encontré con esto por primera vez.

Necesitaba inicializar un miembro de datos estáticos privados en una clase de plantilla.

en .h o .hpp, parece algo como esto para inicializar un miembro de datos estáticos de una clase de plantilla:

template<typename T> Type ClassName<T>::dataMemberName = initialValue;


También puede incluir la asignación en el archivo de encabezado si utiliza protectores de encabezado. He utilizado esta técnica para una biblioteca de C ++ que he creado. Otra forma de lograr el mismo resultado es usar métodos estáticos. Por ejemplo...

class Foo { public: int GetMyStatic() const { return *MyStatic(); } private: static int* MyStatic() { static int mStatic = 0; return &mStatic; } }

El código anterior tiene el "bono" de no requerir un archivo fuente / CPP. De nuevo, un método que utilizo para mis bibliotecas de C ++.


También trabajando en el archivo privateStatic.cpp:

#include <iostream> using namespace std; class A { private: static int v; }; int A::v = 10; // possible initializing int main() { A a; //cout << A::v << endl; // no access because of private scope return 0; } // g++ privateStatic.cpp -o privateStatic && ./privateStatic


Una forma "de la vieja escuela" de definir constantes es reemplazándolas por una enum :

class foo { private: enum {i = 0}; // default type = int enum: int64_t {HUGE = 1000000000000}; // may specify another type };

De esta manera no es necesario proporcionar una definición, y evita hacer que el valor de lvalue constante, lo que puede ahorrarle algunos dolores de cabeza, por ejemplo, cuando lo ODR-use accidentalmente con ODR-use .


Patrón constructor estático que funciona para múltiples objetos.

Se propuso un idioma en: https://.com/a/27088552/895245 pero aquí aparece una versión más limpia que no requiere la creación de un nuevo método por miembro, y un ejemplo ejecutable:

#include <cassert> #include <vector> // Normally on the .hpp file. class MyClass { public: static std::vector<int> v, v2; static struct _StaticConstructor { _StaticConstructor() { v.push_back(1); v.push_back(2); v2.push_back(3); v2.push_back(4); } } _staticConstructor; }; // Normally on the .cpp file. std::vector<int> MyClass::v; std::vector<int> MyClass::v2; // Must come after every static member. MyClass::_StaticConstructor MyClass::_staticConstructor; int main() { assert(MyClass::v[0] == 1); assert(MyClass::v[1] == 2); assert(MyClass::v2[0] == 3); assert(MyClass::v2[1] == 4); }

Ver también: constructores estáticos en C ++? Necesito inicializar objetos estáticos privados

Probado con g++ -std=c++11 -Wall -Wextra , GCC 7.2, Ubuntu 17.10.


int foo::i = 0;

Es la sintaxis correcta para inicializar la variable, pero debe ir en el archivo de origen (.cpp) en lugar de en el encabezado.

Debido a que es una variable estática, el compilador necesita crear una sola copia. Debe tener una línea "int foo: i" en algún lugar de su código para indicar 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, así que obtenga múltiples errores de símbolos definidos del vinculador.