costruttori statici in C ++? Devo inizializzare oggetti statici privati


176

Voglio avere una classe con un membro di dati statici privati ​​(un vettore che contiene tutti i caratteri az). In java o C #, posso semplicemente creare un "costruttore statico" che verrà eseguito prima di creare qualsiasi istanza della classe e impostare i membri di dati statici della classe. Viene eseguito una sola volta (poiché le variabili sono di sola lettura e devono essere impostate una sola volta) e poiché è una funzione della classe, può accedere ai suoi membri privati. Potrei aggiungere codice nel costruttore che controlla se il vettore è inizializzato e inizializzarlo se non lo è, ma introduce molti controlli necessari e non sembra la soluzione ottimale al problema.

Mi viene in mente che, poiché le variabili saranno di sola lettura, possono essere solo costanti statiche pubbliche, quindi posso impostarle una volta fuori dalla classe, ma ancora una volta, sembra una specie di brutto hack.

È possibile avere membri di dati statici privati ​​in una classe se non desidero inizializzarli nel costruttore dell'istanza?



1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 Questa domanda è focalizzata sull'esecuzione di codice per inizializzare oggetti statici privati , non sull'impostazione di valori costanti di tipi primitivi statici privati. Le soluzioni sono diverse.
Gordon Gustafson,

ah, penso che tu abbia ragione, ritirandoti.
Ciro Santilli 5 冠状 病 六四 事件 法轮功

Risposte:


180

Per ottenere l'equivalente di un costruttore statico, è necessario scrivere una classe ordinaria separata per contenere i dati statici e quindi creare un'istanza statica di quella classe 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

};

12
Grazie! anche se è molto fastidioso dover fare tutto questo. Uno dei tanti "errori" che C # e Java hanno imparato.
Gordon Gustafson,

109
Sì. Sottolineo sempre alle persone che se il C ++ non avesse commesso tutti quegli "errori", altre lingue avrebbero dovuto farli. Il C ++ che copre così tanto terreno, anche facendo errori, è stato ottimo per le lingue che lo hanno seguito.
quark,

11
Solo una piccola sfumatura, poiché i costruttori entrano in gioco nessuno garantisce quando viene eseguito il costruttore per l'oggetto statico. Un noto approccio molto più sicuro è la classe Elsewhere {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // il costruttore viene eseguito una volta, quando qualcuno ne ha bisogno per la prima volta restituisce staticStuff; }}; Mi chiedo se i costruttori statici in C # e Java possano fornire la stessa garanzia del codice sopra ...
Oleg Zhylin,

13
@Oleg: Sì, lo fanno. Lo standard garantisce che i costruttori per tutte le variabili non locali vengano eseguiti prima di immettere main. Garantisce inoltre che all'interno di un'unità di compilazione l'ordine di costruzione è ben definito e lo stesso ordine di dichiarazione all'interno dell'unità di compilazione. Sfortunatamente non definiscono l'ordine tra più unità di compilazione.
Martin York,

13
Questo in realtà è un caso in cui friendha molto senso in modo che la classe Elsewherepossa facilmente accedere StaticStuffagli interni (senza interrompere l'incapsulamento in alcun modo pericoloso, potrei aggiungere).
Konrad Rudolph,

81

Bene puoi avere

class MyClass
{
    public:
        static vector<char> a;

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

Non dimenticare (nel .cpp) questo:

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

Il programma continuerà a collegarsi senza la seconda riga, ma l'inizializzatore non verrà eseguito.


+1 (non l'ho provato) Ma: quando viene chiamato ctor _init._init ()? Prima o dopo il ctor di MyClass quando ho un oggetto MyClass statico? Credo che non si può dire ...
ur.

2
ciao, dove posso trovare di più su questo "inizializzatore" magico?
Karel Bílek,

Non dovrebbe essere MyClass::a.push_back(i)invece di a.push_back(i)?
Neel Basu,

4
@ur .: _initializerè un oggetto secondario di MyClass. Gli oggetti secondari vengono inizializzati in questo ordine: oggetti secondari della classe di base virtuale, in ordine di profondità, da sinistra a destra (ma inizializzando ogni oggetto secondario distinto una sola volta); quindi semplici oggetti secondari della classe base, in ordine di profondità, da sinistra a destra; quindi gli oggetti secondari dei membri in ordine di dichiarazione. Quindi è sicuro usare la strategia di EFraim, a condizione che il codice _initialisersi riferisca solo ai membri dichiarati prima.
j_random_hacker,

2
Questa risposta è migliore di quella accettata perché l'autore ha menzionato l'inizializzazione indispensabile nella seconda clip di codice.
Jeff T.

33

Soluzione C ++ 11

Dal C ++ 11, puoi semplicemente usare le espressioni lambda per inizializzare i membri della classe statica. Questo funziona anche se è necessario imporre un ordine di costruzione tra i vari membri statici o se si hanno membri statici che lo sono const.

File di intestazione:

class MyClass {
    static const vector<char> letters;
    static const size_t letterCount;
};

File sorgente:

// Initialize MyClass::letters by using a lambda expression.
const vector<char> MyClass::letters = [] {
    vector<char> letters;
    for (char c = 'a'; c <= 'z'; c++)
        letters.push_back(c);
    return letters;
}();

// The initialization order of static members is defined by the order of
// definition within the source file, so we can access MyClass::letters here.
const size_t MyClass::letterCount = letters.size();

soluzione interessante. in questo caso, se lancio un'eccezione, chi può catturarla?
rafi wiener

5
Il codice di inizializzazione del programma statico non deve mai generare eccezioni, altrimenti il ​​programma si arresterà in modo anomalo. È necessario racchiudere la logica di inizializzazione in un try catchblocco se si potrebbero generare eccezioni.
emkey08

19

Nel file .h:

class MyClass {
private:
    static int myValue;
};

Nel file .cpp:

#include "myclass.h"

int MyClass::myValue = 0;

5
Funziona bene per singoli membri statici (indipendentemente dal tipo). Il difetto rispetto ai costruttori statici è che non è possibile imporre un ordine tra i vari membri statici. Se devi farlo, vedi la risposta di Earwicker.
quark,

Lo sto facendo esattamente, ma non si compila ancora. E dice che questa è l'area del problema (nel costruttore, non nell'intestazione)
Flotolk,

14

Ecco un altro approccio simile a Daniel Earwicker, che utilizza anche il suggerimento della classe di amici di Konrad Rudolph. Qui usiamo una classe di utilità di un amico privato interno per inizializzare i membri statici della tua classe principale. Per esempio:

File di intestazione:

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;
};

File di implementazione:

// 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);
}

Questo approccio ha il vantaggio di nascondere completamente la classe Initializer dal mondo esterno, mantenendo tutto ciò che è contenuto nella classe da inizializzare.


+1 Per fare un esempio che mantiene l'implementazione nel proprio file.
Andrew Larsson,

1
Inoltre, devi assicurarti che ToBeInitialized::Initializer::Initializer()venga chiamato, quindi devi aggiungerlo ToBeInitialized::Initializer ToBeInitialized::initializer;al file di implementazione. Ho preso alcune cose dalla tua idea e dall'idea di EFraim, e funziona esattamente come ne ho bisogno e sembra pulito. Grazie uomo.
Andrew Larsson,

11

Test::StaticTest() viene chiamato esattamente una volta durante l'inizializzazione statica globale.

Il chiamante deve solo aggiungere una linea alla funzione che deve essere il suo costruttore statico.

static_constructor<&Test::StaticTest>::c;forza l'inizializzazione cdurante l'inizializzazione statica globale.

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;
}

Questa è una soluzione fantastica Mi piace molto anche la risposta di Douglas Mandel , ma questa è ancora più concisa.
FlintZA,

Questo è davvero fantastico!
nh_

9

Nessuna necessità di una init()funzione, std::vectorpuò essere creata da un intervallo:

// 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 ) );

Si noti, tuttavia, che le statiche del tipo di classe causano problemi nelle librerie, quindi dovrebbero essere evitate lì.

Aggiornamento C ++ 11

A partire da C ++ 11, puoi invece farlo:

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

È semanticamente equivalente alla soluzione C ++ 98 nella risposta originale, ma non è possibile utilizzare una stringa letterale sul lato destro, quindi non è completamente superiore. Tuttavia, se si dispone di un vettore di qualsiasi altro tipo che char, wchar_t, char16_to char32_t(array di che possono essere scritti come stringhe letterali), la versione C ++ 11 sarà strettamente rimuovere codice standard senza introdurre altre sintassi, rispetto al C ++ 98 versione.


Mi piace. Anche se solo potessimo farlo in una riga senza l'alfabeto ora inutile.
Martin York,

Per causare problemi con le librerie, importa se la classe statica è privata o pubblica? Inoltre, importa se la libreria è statica (.a) o dinamica (.so)?
Zachary Kraus,

@ZacharyKraus: cos'è una classe pubblica / privata ? E no, mentre i problemi sono diversi, ma si sovrappongono, non importa se la libreria è collegata staticamente o dinamicamente.
Marc Mutz - mmutz

@ MarcMutz-mmutz Ci scusiamo per l'uso della classe pubblica / privata che non è una terminologia C ++ corretta. Quello a cui mi riferivo è la soluzione di EFraim sopra. Nella mia versione, tuttavia, ho reso privato il membro della classe statica. Stavo cercando di capire se avere un membro di classe statico come pubblico o privato fa la differenza nello sviluppo e nell'usabilità della biblioteca. Il mio istinto mi dice che non dovrebbe influire sulla libreria perché gli utenti non avranno mai accesso al membro della classe statica o all'oggetto che sta costruendo, ma mi piacerebbe avere un po 'di saggezza del guru su questo argomento.
Zachary Kraus,

@ZacharyKraus: il problema principale con le statiche che richiedono l'inizializzazione dinamica ([basic.start.init] / 2) è che eseguono il codice. Nelle librerie, è possibile che il codice della libreria sia già stato scaricato quando vengono eseguiti i distruttori. Se vuoi saperne di più, ti suggerisco di pubblicare una domanda al riguardo.
Marc Mutz - mmutz,

6

Il concetto di costruttori statici è stato introdotto in Java dopo aver appreso dai problemi in C ++. Quindi non abbiamo un equivalente diretto.

La soluzione migliore è utilizzare tipi di POD che possono essere inizializzati in modo esplicito.
Oppure rendi i tuoi membri statici un tipo specifico con un proprio costruttore che lo inizializzerà correttamente.

//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;

4

Quando provo a compilare e usare la classe Elsewhere(dalla risposta di Earwicker ) ottengo:

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

Sembra che non sia possibile inizializzare gli attributi statici di tipi non interi senza mettere un po 'di codice al di fuori della definizione di classe (CPP).

Per fare quella compilazione puoi invece usare " un metodo statico con una variabile locale statica ". Qualcosa come questo:

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

E puoi anche passare argomenti al costruttore o inizializzarlo con valori specifici, è molto flessibile, potente e facile da implementare ... l'unica cosa è che hai un metodo statico contenente una variabile statica, non un attributo statico ... la sintassi cambia un po ', ma è comunque utile. Spero che questo sia utile per qualcuno,

Hugo González Castro.


Fai attenzione se usi i thread. Credo che in GCC la costruzione di locali statici sia protetta dall'esecuzione simultanea, ma in Visual C ++ non lo è.
Daniel Earwicker,

1
Da C ++ 11 in poi, e in POSIX, deve essere thread-safe.
Marc Mutz - mmutz,

Mi sono piaciute abbastanza altre due soluzioni sopra ( questa e questa ), ma la tua è l'unica che garantisce l'inizializzazione della statica nell'ordine in cui sono necessarie in tutte le librerie. Ho solo un metodo di istanza statica privato come il tuo sopra e avvolgo l'accesso ad altri valori in accessi statici pubblici che usano quel metodo di istanza invece di riferimenti diretti. Grazie.
FlintZA,

Eccezionale! Questo lo completa.
Gabe Halsmer,

4

Immagino che la soluzione semplice a questo sarà:

    //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;
    }

È così che lo faccio anche io.
Etherealone,

1

Ho appena risolto lo stesso trucco. Ho dovuto specificare la definizione di un singolo membro statico per Singleton. Ma rendi le cose più complicate - ho deciso che non voglio chiamare ctor di RandClass () a meno che non lo userò ... ecco perché non volevo inizializzare singleton a livello globale nel mio codice. Inoltre ho aggiunto un'interfaccia semplice nel mio caso.

Ecco il codice finale:

Ho semplificato il codice e utilizzo la funzione rand () e il suo iniziatore a seme singolo 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();
}

1

Ecco la mia variante della soluzione di EFraim; la differenza è che, grazie all'istanziazione implicita del modello, il costruttore statico viene chiamato solo se vengono create istanze della classe e che nessuna definizione nella.cpp è necessaria file (grazie alla magia dell'istanza del modello).

Nel .hfile hai:

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;

Nel .cppfile puoi avere:

void foobar() {
    MyClass foo; // [1]

    for (vector<char>::iterator it = MyClass::a.begin(); it < MyClass::a.end(); ++it) {
        cout << *it;
    }
    cout << endl;
}

Si noti che MyClass::aviene inizializzato solo se la riga [1] è presente, poiché chiama (e richiede l'istanza di) il costruttore, che quindi richiede l'istanza di _initializer.


1

Ecco un altro metodo, in cui il vettore è privato del file che contiene l'implementazione utilizzando uno spazio dei nomi anonimo. È utile per cose come le tabelle di ricerca private dell'implementazione:

#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;
}

Anche se potresti voler dare un nome Ie iqualcosa di un po 'più oscuro in modo da non usarli accidentalmente da qualche parte più in basso nel file.
Jim Hunziker,

1
Ad essere onesti, è difficile capire perché qualcuno vorrebbe usare membri statici privati ​​anziché spazi dei nomi anonimi nei file di implementazione.
Jim Hunziker,

1

Certamente non deve essere complicato come la risposta attualmente accettata (di Daniel Earwicker). La classe è superflua. In questo caso non è necessaria una guerra linguistica.

File .hpp:

vector<char> const & letters();

File .cpp:

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


0

Definisci le variabili statiche dei membri in modo simile al modo in cui definisci i metodi dei membri.

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;

2
La domanda di CrazyJugglerDrummer non riguardava un vecchio tipo di dati semplice e statico :)
jww

0

Per inizializzare una variabile statica, basta farlo all'interno di un file sorgente. Per esempio:

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


//Foo.cpp
int Foo::hello = 1;

La domanda di CrazyJugglerDrummer non riguardava un vecchio tipo di dati semplice e statico :)
jww

0

Che ne dici di creare un modello per imitare il comportamento di 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
    };
};

0

Per casi semplici come qui una variabile statica racchiusa in una funzione di membro statico è quasi altrettanto buona. È semplice e di solito verrà ottimizzato dai compilatori. Tuttavia, ciò non risolve il problema dell'ordine di inizializzazione per oggetti complessi.

#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();
}

0

È una soluzione?

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

0

Un costruttore statico può essere emulato usando una classe amico o una classe nidificata come di seguito.

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();
}

Produzione:

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

Perché stai utilizzando newun array di caratteri solo per perdere immediatamente il puntatore e sovrascriverlo !?
Eric,

0

Wow, non posso credere che nessuno abbia menzionato la risposta più ovvia, e una che imita più da vicino il comportamento del costruttore statico di C #, cioè non viene chiamata finché non viene creato il primo oggetto di quel tipo.

std::call_once()è disponibile in C ++ 11; se non puoi usarlo, può essere fatto con una variabile di classe booleana statica e un'operazione atomica di confronto e scambio. Nel tuo costruttore, vedi se puoi cambiare atomicamente il flag di classe statica da falseatrue , e in tal caso, puoi eseguire il codice di costruzione statica.

Per ulteriore credito, rendilo un flag a 3 vie anziché un valore booleano, ovvero non correre, correre e finire. Quindi tutte le altre istanze di quella classe possono girare-lock fino a quando l'istanza che esegue il costruttore statico non è terminata (cioè emettere un recinto di memoria, quindi impostare lo stato su "esecuzione eseguita"). Lo spin-lock dovrebbe eseguire l'istruzione "pause" del processore, raddoppiare ogni volta l'attesa fino a una soglia, ecc. - tecnica di blocco dello spin piuttosto standard.

In assenza di C ++ 11, questo dovrebbe iniziare.

Ecco alcuni pseudocodici per guidarti. Inserisci questo nella definizione della tua classe:

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

E questo nel tuo costruttore:

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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.