Classes de stockage
Une classe de stockage dans le contexte des déclarations de variables C++ est un spécificateur de type qui régit la durée de vie, la liaison et l’emplacement de mémoire des objets. Un objet donné ne peut avoir qu'une seule classe de stockage. Les variables définies dans un bloc ont un stockage automatique, sauf indication contraire à l’aide des extern
spécificateurs , static
ou thread_local
. Les objets et variables automatiques n’ont pas de liaison ; elles ne sont pas visibles pour le code en dehors du bloc. La mémoire est allouée automatiquement quand l’exécution entre dans le bloc, et elle est désallouée lorsque le bloc est arrêté.
Notes
Le
mutable
mot clé peut être considéré comme un spécificateur de classe de stockage. Toutefois, il n’est disponible que dans la liste des membres d’une définition de classe.Visual Studio 2010 et versions ultérieures : Le
auto
mot clé n’est plus un spécificateur de classe de stockage C++, et leregister
mot clé est déconseillé. Visual Studio 2017 version 15.7 et ultérieures : (disponible en/std:c++17
mode et versions ultérieures) : leregister
mot clé est supprimé du langage C++. Son utilisation provoque un message de diagnostic :// 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
Le static
mot clé peut être utilisé pour déclarer des variables et des fonctions au niveau de l’étendue globale, de l’étendue d’espace de noms et de l’étendue de classe. Les variables statiques peuvent également être déclarées dans la portée locale.
La durée statique signifie que l'objet ou la variable est alloué au démarrage du programme et est libéré à la fin de l'exécution du programme. La liaison externe signifie que le nom de la variable est visible à partir de l’extérieur du fichier où la variable est déclarée. À l’inverse, la liaison interne signifie que le nom n’est pas visible en dehors du fichier où la variable est déclarée. Par défaut, un objet ou une variable défini dans l'espace de noms global a une durée statique et une liaison externe. Le static
mot clé peut être utilisé dans les situations suivantes.
Lorsque vous déclarez une variable ou une fonction au niveau de l’étendue de fichier (étendue globale et/ou d’espace de noms), le
static
mot clé spécifie que la variable ou la fonction a une liaison interne. Lorsque vous déclarez une variable, la variable a une durée statique et le compilateur l'initialise à 0, sauf si vous spécifiez une autre valeur.Lorsque vous déclarez une variable dans une fonction, le
static
mot clé spécifie que la variable conserve son état entre les appels à cette fonction.Lorsque vous déclarez un membre de données dans une déclaration de classe, le
static
mot clé spécifie qu’une copie du membre est partagée par toutes les instances de la classe. Unstatic
membre de données doit être défini au niveau de l’étendue du fichier. Un membre de données intégral que vous déclarez commeconst static
peut avoir un initialiseur.Lorsque vous déclarez une fonction membre dans une déclaration de classe, le
static
mot clé spécifie que la fonction est partagée par toutes les instances de la classe . Unestatic
fonction membre ne peut pas accéder à un membre d’instance, car la fonction n’a pas de pointeur implicitethis
. Pour accéder à un membre d’instance, déclarez la fonction avec un paramètre qui est un pointeur ou une référence d’instance.Vous ne pouvez pas déclarer les membres d’un
union
en tant questatic
. Toutefois, un anonymeunion
déclaré globalement doit être déclaréstatic
explicitement.
Cet exemple montre comment une variable déclarée static
dans une fonction conserve son état entre les appels à cette fonction.
// 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
Cet exemple montre l’utilisation de static
dans une 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
L’exemple suivant montre une variable locale déclarée static
dans une fonction membre. La static
variable est disponible pour l’ensemble du programme ; toutes les instances du type partagent la même copie de la static
variable.
// 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
À compter de C++11, une static
initialisation de variable locale est garantie comme thread-safe. Cette fonctionnalité est parfois appelée statique magique. Toutefois, dans une application multithread, toutes les assignations suivantes doivent être synchronisées. La fonctionnalité d’initialisation statique thread-safe peut être désactivée à l’aide de l’indicateur /Zc:threadSafeInit-
pour éviter de dépendre du CRT.
extern
Les objets et les variables déclarés comme extern
déclarant un objet défini dans une autre unité de traduction ou dans une étendue englobante comme ayant une liaison externe. Pour plus d’informations, consultez extern
unités de traduction et liaison.
thread_local
(C++11)
Une variable déclarée avec le thread_local
spécificateur est accessible uniquement sur le thread sur lequel elle est créée. La variable est créée lors de la création du thread, et elle est détruite lorsque le thread est détruit. Chaque thread possède sa propre copie de la variable. Sur Windows, thread_local
est fonctionnellement équivalent à l’attribut spécifique __declspec( thread )
à 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;
}
Éléments à noter sur le thread_local
spécificateur :
Les variables locales de thread initialisées dynamiquement dans les DLL peuvent ne pas être correctement initialisées sur tous les threads appelants. Pour plus d’informations, consultez
thread
.Le
thread_local
spécificateur peut être combiné avecstatic
ouextern
.Vous ne pouvez appliquer
thread_local
qu’aux définitions et déclarations de données;thread_local
ne peut pas être utilisé sur les déclarations ou définitions de fonction.Vous pouvez spécifier
thread_local
uniquement sur les éléments de données avec une durée de stockage statique, qui inclut les objets de données globaux (à la fois etextern
), lesstatic
objets statiques locaux et les membres de données statiques des classes. Toute variable locale déclaréethread_local
est implicitement statique si aucune autre classe de stockage n’est fournie ; en d’autres termes, au niveau de l’étenduethread_local
du bloc est équivalent àthread_local static
.Vous devez spécifier
thread_local
à la fois pour la déclaration et la définition d'un objet local de thread, que la déclaration et la définition se produisent dans le même fichier ou dans des fichiers séparés.Nous vous déconseillons d’utiliser des
thread_local
variables avecstd::launch::async
. Pour plus d’informations, consultez<future>
Fonctions.
Sur Windows, thread_local
est fonctionnellement équivalent à , __declspec(thread)
sauf que *__declspec(thread)
* peut être appliqué à une définition de type et est valide dans le code C. Dans la mesure du possible, utilisez thread_local
car il fait partie de la norme C++ et est donc plus portable.
inscription
Visual Studio 2017 version 15.3 et ultérieures (disponible en /std:c++17
mode et versions ultérieures) : le register
mot clé n’est plus une classe de stockage prise en charge. Son utilisation entraîne un diagnostic. Le mot clé est toujours réservé dans la norme pour une utilisation ultérieure.
register int val; // warning C5033: 'register' is no longer a supported storage class
Exemple : initialisation automatique et statique
Une variable automatique ou un objet local est initialisé chaque fois que l'ordre d'exécution atteint sa définition. Une variable automatique ou un objet statique est initialisé la première fois que l'ordre d'exécution atteint sa définition.
Prenons l'exemple suivant, qui définit une classe qui stocke l'initialisation et la destruction des objets, puis définit trois objets, I1
, I2
, et 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
Cet exemple montre comment et quand les objets I1
, I2
et I3
sont initialisés et quand ils sont détruits.
Il y a plusieurs points à noter sur le programme :
Tout d’abord,
I1
etI2
sont automatiquement détruits lorsque le flux de contrôle quitte le bloc dans lequel ils sont définis.Deuxièmement, en C++, il n’est pas nécessaire de déclarer des objets ou des variables au début d’un bloc. En outre, ces objets sont initialisés uniquement lorsque l'ordre d'exécution atteint leurs définitions. (
I2
etI3
sont des exemples de ces définitions.) La sortie indique exactement quand ils sont initialisés.Enfin, les variables locales statiques telles que
I3
conserver leurs valeurs pendant l’exécution du programme, mais sont détruites à l’arrêt du programme.