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 spécificateurs ou thread_local
des extern
static
spécificateurs. Les objets et variables automatiques n’ont aucune liaison ; ils ne sont pas visibles pour le code en dehors du bloc. La mémoire est allouée automatiquement lorsque l’exécution entre dans le bloc et qu’elle est dé-allouée lorsque le bloc est arrêté.
Notes
La
mutable
mot clé peut être considérée comme un spécificateur de classe de stockage. Toutefois, il est disponible uniquement 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érieure : (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 dans l’étendue globale, l’étendue de l’espace de noms et 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. La static
mot clé peut être utilisée 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), l’mot clé
static
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, la
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, l’mot clé
static
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. Membre de données intégral que vous déclarez commeconst static
pouvant avoir un initialiseur.Lorsque vous déclarez une fonction membre dans une déclaration de classe, l’mot clé
static
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 d’instance ou une référence.Vous ne pouvez pas déclarer les membres d’un
union
asstatic
. 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 dans static
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 d’être 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 prendre une dépendance sur le CRT.
extern
Les objets et les variables déclarés comme extern
déclarent 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
et 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 il est créé. La variable est créée lorsque le thread est créé et détruit 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;
}
Remarques 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 s’appliquer
thread_local
qu’aux déclarations et définitions de données ;thread_local
ne peut pas être utilisé sur les déclarations de fonction ou les définitions.Vous pouvez spécifier
thread_local
uniquement sur les éléments de données avec une durée de stockage statique, qui inclut des objets de données globaux (à la foisstatic
etextern
), des objets statiques locaux et des membres de données statiques de classes. Toute variable locale déclaréethread_local
est implicitement statique si aucune autre classe de stockage n’est fournie ; en d’autres termes, l’étenduethread_local
de bloc est équivalente à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
thread_local
des variables avecstd::launch::async
. Pour plus d’informations, consultez<future>
les fonctions.
Sur Windows, thread_local
équivaut fonctionnellement à __declspec(thread)
ceci près que *__declspec(thread)
* peut être appliqué à une définition de type et est valide dans le code C. Dans la mesure du possible, utilisez-le thread_local
parce qu’il fait partie de la norme C++ et est donc plus portable.
inscription
Visual Studio 2017 version 15.3 et ultérieure (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 provoque 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 existe 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 s’affiche exactement quand elles sont initialisées.Enfin, les variables locales statiques telles que
I3
conserver leurs valeurs pendant l’exécution du programme, mais sont détruites à mesure que le programme se termine.
Voir aussi
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour