Condividi tramite


Classi di archiviazione

Una classe di archiviazione nel contesto delle dichiarazioni di variabili C++ è un identificatore di tipo che regola la durata, il collegamento e la posizione di memoria degli oggetti. Un oggetto specificato può avere una sola classe di archiviazione. Le variabili definite all'interno di un blocco hanno una risorsa di archiviazione automatica, a meno che non diversamente specificato usando gli externidentificatori , statico thread_local . Gli oggetti automatici e le variabili non hanno alcun collegamento; non sono visibili al codice all'esterno del blocco. La memoria viene allocata automaticamente quando l'esecuzione entra nel blocco e viene deallocata quando il blocco viene chiuso.

Note

  • La mutable parola chiave può essere considerata un identificatore di classe di archiviazione. Tuttavia, è disponibile solo nell'elenco dei membri di una definizione di classe.

  • Visual Studio 2010 e versioni successive: la auto parola chiave non è più un identificatore di classe di archiviazione C++ e la register parola chiave è deprecata. Visual Studio 2017 versione 15.7 e successive: (disponibile in /std:c++17 modalità e versioni successive): la register parola chiave viene rimossa dal linguaggio C++. L'uso causa un messaggio di diagnostica:

    // c5033.cpp
    // compile by using: cl /c /std:c++17 c5033.cpp
    register int value; // warning C5033: 'register' is no longer a supported storage class
    

static

La static parola chiave può essere usata per dichiarare variabili e funzioni nell'ambito globale, nell'ambito dello spazio dei nomi e nell'ambito della classe. Le variabili statiche possono essere dichiarate anche in ambito locale.

La durata statica significa che l'oggetto o la variabile viene allocata all'avvio del programma e viene rilasciata alla chiusura del programma. Il collegamento esterno indica che il nome della variabile è visibile dall'esterno del file in cui viene dichiarata la variabile. Viceversa, il collegamento interno indica che il nome non è visibile all'esterno del file in cui viene dichiarata la variabile. Per impostazione predefinita, un oggetto o una variabile definiti nello spazio dei nomi globale presentano una durata e un collegamento esterno statici. La static parola chiave può essere usata nelle situazioni seguenti.

  1. Quando si dichiara una variabile o una funzione nell'ambito del file (ambito globale e/o dello spazio dei nomi), la static parola chiave specifica che la variabile o la funzione ha un collegamento interno. Quando si dichiara una variabile, questa ha una durata statica e il compilatore la inizializza con il valore 0 a meno che non venga specificato un altro valore.

  2. Quando si dichiara una variabile in una funzione, la static parola chiave specifica che la variabile mantiene lo stato tra le chiamate a tale funzione.

  3. Quando si dichiara un membro dati in una dichiarazione di classe, la static parola chiave specifica che una copia del membro viene condivisa da tutte le istanze della classe . Un static membro dati deve essere definito nell'ambito del file. Un membro dati integrale dichiarato come const static può avere un inizializzatore.

  4. Quando si dichiara una funzione membro in una dichiarazione di classe, la static parola chiave specifica che la funzione è condivisa da tutte le istanze della classe . Una static funzione membro non può accedere a un membro dell'istanza perché la funzione non ha un puntatore implicito this . Per accedere a un membro dell'istanza, dichiarare la funzione con un parametro che rappresenta un puntatore o un riferimento all'istanza.

  5. Non è possibile dichiarare i membri di un union oggetto come static. Tuttavia, un anonimo union dichiarato a livello globale deve essere dichiarato staticin modo esplicito.

In questo esempio viene illustrato come una variabile dichiarata static in una funzione mantenga lo stato tra le chiamate a tale funzione.

// static1.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
void showstat( int curr ) {
   static int nStatic;    // Value of nStatic is retained
                          // between each function call
   nStatic += curr;
   cout << "nStatic is " << nStatic << endl;
}

int main() {
   for ( int i = 0; i < 5; i++ )
      showstat( i );
}
nStatic is 0
nStatic is 1
nStatic is 3
nStatic is 6
nStatic is 10

In questo esempio viene illustrato l'uso di static in una classe .

// static2.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
class CMyClass {
public:
   static int m_i;
};

int CMyClass::m_i = 0;
CMyClass myObject1;
CMyClass myObject2;

int main() {
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject1.m_i = 1;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject2.m_i = 2;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   CMyClass::m_i = 3;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;
}
0
0
1
1
2
2
3
3

Nell'esempio seguente viene illustrata una variabile locale dichiarata static in una funzione membro. La static variabile è disponibile per l'intero programma. Tutte le istanze del tipo condividono la stessa copia della static variabile.

// static3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct C {
   void Test(int value) {
      static int var = 0;
      if (var == value)
         cout << "var == value" << endl;
      else
         cout << "var != value" << endl;

      var = value;
   }
};

int main() {
   C c1;
   C c2;
   c1.Test(100);
   c2.Test(100);
}
var != value
var == value

A partire da C++11, l'inizializzazione di una static variabile locale è garantita come thread-safe. Questa funzionalità è talvolta denominata statici magic. In un'applicazione con multithreading, tuttavia, tutte le assegnazioni successive devono essere sincronizzate. La funzionalità di inizializzazione statica thread-safe può essere disabilitata usando il /Zc:threadSafeInit- flag per evitare di assumere una dipendenza da CRT.

extern

Oggetti e variabili dichiarati come extern dichiarano un oggetto definito in un'altra unità di conversione o in un ambito di inclusione come collegamento esterno. Per altre informazioni, vedere extern Unità di traduzione e collegamento.

thread_local (C++11)

Una variabile dichiarata con l'identificatore thread_local è accessibile solo nel thread in cui viene creato. La variabile viene creata al momento della creazione del thread e viene eliminata definitivamente quando il thread viene eliminato definitivamente. Ogni thread ha la propria copia della variabile. In Windows thread_local è funzionalmente equivalente all'attributo specifico __declspec( thread ) di Microsoft.

thread_local float f = 42.0; // Global namespace. Not implicitly static.

struct S // cannot be applied to type definition
{
    thread_local int i; // Illegal. The member must be static.
    thread_local static char buf[10]; // OK
};

void DoSomething()
{
    // Apply thread_local to a local variable.
    // Implicitly "thread_local static S my_struct".
    thread_local S my_struct;
}

Aspetti da notare sull'identificatore thread_local :

  • Le variabili thread-local in DLL inizializzate dinamicamente potrebbero non essere inizializzate correttamente in tutti i thread chiamanti. Per ulteriori informazioni, vedere thread.

  • L'identificatore thread_local può essere combinato con static o extern.

  • È possibile applicare thread_local solo alle dichiarazioni e alle definizioni dei dati. thread_local Non può essere usato nelle dichiarazioni o nelle definizioni di funzione.

  • È possibile specificare thread_local solo sugli elementi di dati con durata di archiviazione statica, che include oggetti dati globali (e staticextern), oggetti statici locali e membri dati statici delle classi. Qualsiasi variabile locale dichiarata thread_local è implicitamente statica se non viene fornita alcuna altra classe di archiviazione; in altre parole, l'ambito thread_local del blocco equivale a thread_local static.

  • È necessario specificare thread_local per la dichiarazione e la definizione di un oggetto locale di thread, indipendentemente dal fatto che la definizione e la dichiarazione si verificano nello stesso file o in file separati.

  • Non è consigliabile usare thread_local le variabili con std::launch::async. Per altre informazioni, vedere <future> Funzioni.

In Windows thread_local , è funzionalmente equivalente a __declspec(thread) , ad eccezione del fatto che *__declspec(thread)* può essere applicato a una definizione di tipo ed è valido nel codice C. Quando possibile, usare thread_local perché fa parte dello standard C++ ed è quindi più portabile.

register

Visual Studio 2017 versione 15.3 e successive (disponibile in /std:c++17 modalità e versioni successive): la register parola chiave non è più una classe di archiviazione supportata. L'uso di genera una diagnostica. La parola chiave è ancora riservata nello standard per un uso futuro.

   register int val; // warning C5033: 'register' is no longer a supported storage class

Esempio: inizializzazione automatica e statica

Ogni volta che il flusso di controllo raggiunge la propria definizione, vengono inizializzati un oggetto o una variabile locali automatici. La prima volta che il flusso di controllo raggiunge la relativa definizione, vengono inizializzati un oggetto o una variabile statici.

Si consideri l'esempio seguente, nel quale si definisce una classe che registra l'inizializzazione e l'eliminazione di oggetti e, in seguito, definisce tre oggetti, I1, I2 e I3:

// initialization_of_objects.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
using namespace std;

// Define a class that logs initializations and destructions.
class InitDemo {
public:
    InitDemo( const char *szWhat );
    ~InitDemo();

private:
    char *szObjName;
    size_t sizeofObjName;
};

// Constructor for class InitDemo
InitDemo::InitDemo( const char *szWhat ) :
    szObjName(NULL), sizeofObjName(0) {
    if ( szWhat != 0 && strlen( szWhat ) > 0 ) {
        // Allocate storage for szObjName, then copy
        // initializer szWhat into szObjName, using
        // secured CRT functions.
        sizeofObjName = strlen( szWhat ) + 1;

        szObjName = new char[ sizeofObjName ];
        strcpy_s( szObjName, sizeofObjName, szWhat );

        cout << "Initializing: " << szObjName << "\n";
    }
    else {
        szObjName = 0;
    }
}

// Destructor for InitDemo
InitDemo::~InitDemo() {
    if( szObjName != 0 ) {
        cout << "Destroying: " << szObjName << "\n";
        delete szObjName;
    }
}

// Enter main function
int main() {
    InitDemo I1( "Auto I1" ); {
        cout << "In block.\n";
        InitDemo I2( "Auto I2" );
        static InitDemo I3( "Static I3" );
    }
    cout << "Exited block.\n";
}
Initializing: Auto I1
In block.
Initializing: Auto I2
Initializing: Static I3
Destroying: Auto I2
Exited block.
Destroying: Auto I1
Destroying: Static I3

In questo esempio viene illustrato come e quando gli oggetti I1, I2e I3 vengono inizializzati e quando vengono eliminati definitivamente.

Ci sono diversi punti da notare sul programma:

  • Prima di tutto, I1 e I2 vengono eliminati automaticamente quando il flusso di controllo esce dal blocco in cui sono definiti.

  • In secondo luogo, in C++, non è necessario dichiarare oggetti o variabili all'inizio di un blocco. Questi oggetti, inoltre, vengono inizializzati solo quando il flusso di controllo raggiunge le relative definizioni. (I2 e I3 sono esempi di tali definizioni. L'output mostra esattamente quando vengono inizializzati.

  • Infine, le variabili locali statiche, ad I3 esempio, mantengono i valori durante l'esecuzione del programma, ma vengono eliminate definitivamente al termine del programma.

Vedi anche

Dichiarazioni e definizioni