variable usar objetos estatica cuando c++ static private initializer static-constructor

c++ - usar - variable estatica



constructores estáticos en C++? Necesito inicializar objetos estáticos privados (21)

Quiero tener una clase con un miembro privado de datos estáticos (un vector que contiene todos los caracteres az). En java o C #, puedo hacer un "constructor estático" que se ejecutará antes de hacer cualquier instancia de la clase, y configura los miembros de datos estáticos de la clase. Solo se ejecuta una vez (ya que las variables son de solo lectura y solo deben establecerse 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 está inicializado e inicializarlo si no es así, pero eso introduce muchas comprobaciones necesarias y no parece ser la solución óptima al problema.

Se me ocurre la idea de que, dado que las variables serán de solo lectura, pueden ser const públicas, por lo que puedo configurarlas una vez fuera de la clase, pero una vez más, parece una especie de hack feo.

¿Es posible tener miembros privados de datos estáticos en una clase si no quiero inicializarlos en el constructor de la instancia?


Construcción estática en C ++ 11

Desde C ++ 11, la construcción estática se puede implementar a través de los inicializadores de función lambda. Ya no tienes que usar ninguna clase de ayuda para esto.

Constructor miembro estático

Archivo de cabecera:

class MyClass { static vector<char> letters; };

Archivo fuente:

vector<char> MyClass::letters = [] { vector<char> letters; for (char c = ''a''; c <= ''z''; c++) { letters.push_back(c); } return letters; }();

Constructor de clase estática

Archivo de cabecera:

class Foo { static Bar staticMember1; static Bar staticMember2; static const bool StaticCtor; // Note: must be the last static member };

Archivo fuente:

Bar Foo::staticMember1; Bar Foo::staticMember2; const bool Foo::StaticCtor = [] { staticMember1 = ... ; staticMember2 = ... ; return true; // Note: unused dummy return value }();


¿Es esto una solución?

class Foo { public: size_t count; Foo() { static size_t count = 0; this->count = count += 1; } };


¿Qué hay de crear una plantilla para imitar el comportamiento de C #.

template<class T> class StaticConstructor { bool m_StaticsInitialised = false; public: typedef void (*StaticCallback)(void); StaticConstructor(StaticCallback callback) { if (m_StaticsInitialised) return; callback(); m_StaticsInitialised = true; } } template<class T> bool StaticConstructor<T>::m_StaticsInitialised; class Test : public StaticConstructor<Test> { static std::vector<char> letters_; static void _Test() { for (char c = ''a''; c <= ''z''; c++) letters_.push_back(c); } public: Test() : StaticConstructor<Test>(&_Test) { // non static stuff }; };


Acabo de resolver el mismo truco. Tuve que especificar la definición de un único miembro estático para Singleton. Pero haz las cosas más complicadas: he decidido que no quiero llamar a ctor de RandClass () a menos que vaya a usarlo ... es por eso que no quería inicializar singleton globalmente en mi código. También agregué una interfaz simple en mi caso.

Aquí está el código final:

Simplifiqué el código y uso la función rand () y su inicializador de inicialización srand ()

interface IRandClass { public: virtual int GetRandom() = 0; }; class RandClassSingleton { private: class RandClass : public IRandClass { public: RandClass() { srand(GetTickCount()); }; virtual int GetRandom(){return rand();}; }; RandClassSingleton(){}; RandClassSingleton(const RandClassSingleton&); // static RandClass m_Instance; // If you declare m_Instance here you need to place // definition for this static object somewhere in your cpp code as // RandClassSingleton::RandClass RandClassSingleton::m_Instance; public: static RandClass& GetInstance() { // Much better to instantiate m_Instance here (inside of static function). // Instantiated only if this function is called. static RandClass m_Instance; return m_Instance; }; }; main() { // Late binding. Calling RandClass ctor only now IRandClass *p = &RandClassSingleton::GetInstance(); int randValue = p->GetRandom(); } abc() { IRandClass *same_p = &RandClassSingleton::GetInstance(); }


Aquí está mi variante de la solución de EFraim; la diferencia es que, gracias a la creación de instancias de plantillas implícitas, solo se llama al constructor estático si se crean instancias de la clase, y que no se necesita ninguna definición en el archivo .cpp (gracias a la magia de instanciación de plantillas).

En el archivo .h , tienes:

template <typename Aux> class _MyClass { public: static vector<char> a; _MyClass() { (void) _initializer; //Reference the static member to ensure that it is instantiated and its initializer is called. } private: static struct _init { _init() { for(char i=''a''; i<=''z''; i++) a.push_back(i); } } _initializer; }; typedef _MyClass<void> MyClass; template <typename Aux> vector<char> _MyClass<Aux>::a; template <typename Aux> typename _MyClass<Aux>::_init _MyClass<Aux>::_initializer;

En el archivo .cpp , puede tener:

void foobar() { MyClass foo; // [1] for (vector<char>::iterator it = MyClass::a.begin(); it < MyClass::a.end(); ++it) { cout << *it; } cout << endl; }

Tenga en cuenta que MyClass::a se inicializa solo si la línea [1] está allí, porque eso llama (y requiere la creación de instancias de) el constructor, que luego requiere la instanciación de _initializer .


Aquí hay otro enfoque similar al de Daniel Earwicker, que también utiliza la sugerencia de la clase de amigo de Konrad Rudolph. Aquí usamos una clase de utilidad privada interna para inicializar los miembros estáticos de su clase principal. Por ejemplo:

Archivo de cabecera:

class ToBeInitialized { // Inner friend utility class to initialize whatever you need class Initializer { public: Initializer(); }; friend class Initializer; // Static member variables of ToBeInitialized class static const int numberOfFloats; static float *theFloats; // Static instance of Initializer // When this is created, its constructor initializes // the ToBeInitialized class'' static variables static Initializer initializer; };

Archivo de implementación:

// Normal static scalar initializer const int ToBeInitialized::numberOfFloats = 17; // Constructor of Initializer class. // Here is where you can initialize any static members // of the enclosing ToBeInitialized class since this inner // class is a friend of it. ToBeInitialized::Initializer::Initializer() { ToBeInitialized::theFloats = (float *)malloc(ToBeInitialized::numberOfFloats * sizeof(float)); for (int i = 0; i < ToBeInitialized::numberOfFloats; ++i) ToBeInitialized::theFloats[i] = calculateSomeFancyValue(i); }

Este enfoque tiene la ventaja de ocultar por completo la clase Initializer del mundo exterior, manteniendo todo lo contenido dentro de la clase para inicializar.


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:

#include <iostream> #include <vector> using namespace std; namespace { vector<int> vec; struct I { I() { vec.push_back(1); vec.push_back(3); vec.push_back(5); }} i; } int main() { vector<int>::const_iterator end = vec.end(); for (vector<int>::const_iterator i = vec.begin(); i != end; ++i) { cout << *i << endl; } return 0; }


Bueno, puedes tener

class MyClass { public: static vector<char> a; static class _init { public: _init() { for(char i=''a''; i<=''z''; i++) a.push_back(i); } } _initializer; };

No olvide (en el .cpp) esto:

vector<char> MyClass::a; MyClass::_init MyClass::_initializer;

El programa seguirá enlazando sin la segunda línea, pero el inicializador no se ejecutará.


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:

vector<char> const & letters();

archivo .cpp:

vector<char> const & letters() { static vector<char> v = {''a'', ''b'', ''c'', ...}; return v; }


Cuando intento compilar y usar la clase Elsewhere (de la respuesta de Earwicker ) obtengo:

error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)

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:

class Elsewhere { public: static StaticStuff& GetStaticStuff() { static StaticStuff staticStuff; // constructor runs once, single instance return staticStuff; } };

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.


Defina variables miembro estáticas de forma similar a la forma en que define los métodos miembro.

foo.h

class Foo { public: void bar(); private: static int count; };

foo.cpp

#include "foo.h" void Foo::bar() { // method definition } int Foo::count = 0;


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 se puedan inicializar explícitamente.
O bien, cree sus miembros estáticos como un tipo específico que tenga su propio constructor que lo inicialice correctamente.

//header class A { // Make sure this is private so that nobody can missues the fact that // you are overriding std::vector. Just doing it here as a quicky example // don''t take it as a recomendation for deriving from vector. class MyInitedVar: public std::vector<char> { public: MyInitedVar() { // Pre-Initialize the vector. for(char c = ''a'';c <= ''z'';++c) { push_back(c); } } }; static int count; static MyInitedVar var1; }; //source int A::count = 0; A::MyInitedVar A::var1;


En el archivo .h:

class MyClass { private: static int myValue; };

En el archivo .cpp:

#include "myclass.h" int MyClass::myValue = 0;


No se necesita una función init() , std::vector se puede crear a partir de un rango:

// h file: class MyClass { static std::vector<char> alphabet; // ... }; // cpp file: #include <boost/range.hpp> static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; std::vector<char> MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) );

Tenga en cuenta, sin embargo, que la estática de tipo de clase causa problemas en las bibliotecas, por lo que deben evitarse allí.

Actualización C ++ 11

A partir de C ++ 11, puede hacer esto en su lugar:

// cpp file: std::vector<char> MyClass::alphabet = { ''a'', ''b'', ''c'', ..., ''z'' };

Es semánticamente equivalente a la solución C ++ 98 en la respuesta original, pero no se puede usar una cadena literal en el lado derecho, por lo que no es completamente superior. Sin embargo, si tiene un vector de cualquier tipo que no sea char , wchar_t , char16_t o char32_t (los arreglos de los cuales se pueden escribir como literales de cadena), la versión C ++ 11 eliminará estrictamente el código repetitivo sin introducir otra sintaxis, en comparación con el C ++ 98 versión.


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á optimizado por los compiladores. Sin embargo, esto no resuelve el problema de orden de inicialización para objetos complejos.

#include <iostream> class MyClass { static const char * const letters(void){ static const char * const var = "abcdefghijklmnopqrstuvwxyz"; return var; } public: void show(){ std::cout << letters() << "/n"; } }; int main(){ MyClass c; c.show(); }


Para inicializar una variable estática, solo debe hacerlo dentro de un archivo fuente. Por ejemplo:

//Foo.h class Foo { private: static int hello; }; //Foo.cpp int Foo::hello = 1;


Para obtener el equivalente de un constructor estático, necesita escribir una clase ordinaria separada para contener los datos estáticos y luego crear una instancia estática de esa clase ordinaria.

class StaticStuff { std::vector<char> letters_; public: StaticStuff() { for (char c = ''a''; c <= ''z''; c++) letters_.push_back(c); } // provide some way to get at letters_ }; class Elsewhere { static StaticStuff staticStuff; // constructor runs once, single instance };


Se puede emular un constructor estático utilizando una clase de amigo o una clase anidada como se muestra a continuación.

class ClassStatic{ private: static char *str; public: char* get_str() { return str; } void set_str(char *s) { str = s; } // A nested class, which used as static constructor static class ClassInit{ public: ClassInit(int size){ // Static constructor definition str = new char[size]; str = "How are you?"; } } initializer; }; // Static variable creation char* ClassStatic::str; // Static constructor call ClassStatic::ClassInit ClassStatic::initializer(20); int main() { ClassStatic a; ClassStatic b; std::cout << "String in a: " << a.get_str() << std::endl; std::cout << "String in b: " << b.get_str() << std::endl; a.set_str("I am fine"); std::cout << "String in a: " << a.get_str() << std::endl; std::cout << "String in b: " << b.get_str() << std::endl; std::cin.ignore(); }

Salida:

String in a: How are you? String in b: How are you? String in a: I am fine String in b: I am fine


Supongo que la solución simple para esto será:

//X.h #pragma once class X { public: X(void); ~X(void); private: static bool IsInit; static bool Init(); }; //X.cpp #include "X.h" #include <iostream> X::X(void) { } X::~X(void) { } bool X::IsInit(Init()); bool X::Init() { std::cout<< "ddddd"; return true; } // main.cpp #include "X.h" int main () { return 0; }


Vaya, no puedo creer que nadie mencionó la respuesta más obvia, y la que más imita 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, puede hacerlo con una variable de clase booleana estática y una operación atómica de comparación e intercambio. En su constructor, vea si puede cambiar atómicamente el indicador de clase estática de false a true , y si es así, puede ejecutar el código de construcción estática.

Para obtener crédito adicional, conviértalo en un marcador de 3 vías en lugar de un booleano, es decir, no ejecutar, ejecutar y listo. Luego, todas las demás instancias de esa clase pueden girar hasta que la instancia que ejecuta el constructor estático haya finalizado (es decir, emitir una valla de memoria, luego establecer el estado en "ejecutado en ejecución"). 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 giro bastante estándar.

En ausencia de C ++ 11, this debería comenzar.

Aquí hay un pseudocódigo para guiarte. Pon esto en tu definición de clase:

enum EStaticConstructor { kNotRun, kRunning, kDone }; static volatile EStaticConstructor sm_eClass = kNotRun;

Y esto en tu constructor:

while (sm_eClass == kNotRun) { if (atomic_compare_exchange_weak(&sm_eClass, kNotRun, kRunning)) { /* Perform static initialization here. */ atomic_thread_fence(memory_order_release); sm_eClass = kDone; } } while (sm_eClass != kDone) atomic_pause();


Test::StaticTest() se llama exactamente una vez durante la inicialización estática global.

El 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 de c durante la inicialización estática global.

template<void(*ctor)()> struct static_constructor { struct constructor { constructor() { ctor(); } }; static constructor c; }; template<void(*ctor)()> typename static_constructor<ctor>::constructor static_constructor<ctor>::c; ///////////////////////////// struct Test { static int number; static void StaticTest() { static_constructor<&Test::StaticTest>::c; number = 123; cout << "static ctor" << endl; } }; int Test::number; int main(int argc, char *argv[]) { cout << Test::number << endl; return 0; }