C++ Classes de stockage
Les classes de stockage définissent la portée (visibilité) et la durée de vie des variables/fonctions dans les programmes C++. Ces spécificateurs sont placés avant le type qu'ils modifient. La liste suivante répertorie les classes de stockage disponibles dans les programmes C++ :
auto: Il s'agit du spécificateur de classe de stockage par défaut et il peut généralement être omis. Les variables spécifiées par auto ont une durée de stockage automatique, c'est-à-dire que leur durée de vie est limitée au bloc dans lequel elles sont définies. Les variables auto sont généralement allouées sur la pile.
register: Utilisé pour suggérer au compilateur de stocker la variable dans un registre du processeur afin d'améliorer la vitesse d'accès. En C++11 et versions ultérieures, register est une fonctionnalité obsolète et n'a plus d'effet pratique.
static: Utilisé pour définir des variables ou des fonctions ayant une durée de stockage statique, dont la durée de vie s'étend sur toute l'exécution du programme. À l'intérieur d'une fonction, la valeur d'une variable static reste inchangée entre les appels de fonction. Dans un fichier ou à la portée globale, les variables static ont une liaison interne et ne sont accessibles que dans le fichier où elles sont définies.
extern: Utilisé pour déclarer des variables ou des fonctions ayant une liaison externe, qui peuvent être partagées entre plusieurs fichiers. Par défaut, les variables globales et les fonctions ont la classe de stockage extern. L'utilisation de extern dans un fichier pour déclarer une variable globale ou une fonction définie dans un autre fichier permet le partage entre fichiers.
mutable (C++11): Utilisé pour modifier des variables membres d'une classe, ce qui permet de modifier leurs valeurs dans des fonctions membres const. Il est couramment utilisé pour des données telles que des caches ou des compteurs qui doivent être modifiés dans un contexte const.
thread_local (C++11): utilisé pour définir des variables avec une durée de stockage locale au thread ; chaque thread possède sa propre copie indépendante. La durée de vie d’une variable locale au thread est identique à la durée de vie du thread.
Depuis C++17, le mot-clé auto n’est plus un spécificateur de classe de stockage C++, et le mot-clé register a été déprécié.
Les spécificateurs de classe de stockage qui y sont présentés offrent aux programmeurs un moyen de contrôler la durée de vie et la visibilité des variables et des fonctions.
Une utilisation raisonnable des spécificateurs de classe de stockage peut améliorer la maintenabilité et les performances du programme.
Depuis C++11, register a perdu son rôle d’origine, tandis que mutable et thread_local sont des fonctionnalités nouvellement introduites utilisées pour résoudre des problèmes de programmation spécifiques.
Voici un exemple illustrant différents spécificateurs de classe de stockage :
Exemple
// Variable globale avec liaison externe, la classe de stockage par défaut est extern
int globalVar;
void function() {
// Variable locale avec une durée de stockage automatique, la classe de stockage par défaut est auto
auto int localVar = 10;
// Variable statique avec une durée de stockage statique, sa durée de vie couvre l’ensemble du programme
static int staticVar = 20;
const int constVar = 30; // une variable const a une durée de stockage statique par défaut
// Tentative de modification d'une variable const, erreur de compilation
// constVar = 40;
// variable membre mutable, peut être modifiée dans les fonctions membres const
class MyClass {
public:
mutable int mutableVar;
void constMemberFunc() const {
mutableVar = 50; // Permet de modifier une variable membre mutable
}
};
// Variable locale au thread, chaque thread possède sa propre copie indépendante
thread_local int threadVar = 60;
}
int main() {
extern int externalVar; // Déclare une variable avec liaison externe
fonction();
return 0;
}
classe de stockage auto
Depuis C++ 11,autoLe mot-clé est utilisé dans deux situations : lors de la déclaration d'une variable, en déduisant automatiquement le type de la variable à partir de l'expression d'initialisation ; lors de la déclaration d'une fonction, comme espace réservé pour la valeur de retour de la fonction.
Dans la norme C++98, le mot-clé auto était utilisé pour la déclaration de variables automatiques, mais comme il était rarement utilisé et redondant, cet usage a été supprimé en C++17.
Déduire automatiquement le type de la variable déclarée à partir de l'expression d'initialisation, par exemple :
classe de stockage register
register est une classe de stockage utilisée pour déclarer des variables et indiquer au compilateur de stocker ces variables dans des registres pour un accès rapide.
L'utilisation du mot-clé register peut améliorer la vitesse d'exécution d'un programme, car elle réduit le nombre d'accès à la mémoire.
Cependant, notez que la classe de stockage register n'est qu'une indication ; le compilateur peut l'ignorer, car les compilateurs modernes optimisent généralement automatiquement le code et choisissent un emplacement de stockage approprié.
Syntaxe :
register data_type variable_name;
registerC'est un mot-clé de classe de stockage, utilisé pour indiquer au compilateur de stocker des variables dans des registres.data_typeC'est le type de données de la variable, qui peut être n'importe quel type de données C++ valide.variable_nameest le nom de la variable.
La classe de stockage register est utilisée pour indiquer au compilateur de stocker les variables dans des registres afin d'améliorer la vitesse d'accès. Cependant, en raison des capacités d'optimisation automatique des compilateurs modernes, l'utilisation du mot-clé register n'est pas nécessaire et est rarement utilisée en pratique.
Dans la norme C++11, le mot-clé register n'est plus un spécificateur de classe de stockage mais une fonctionnalité obsolète. Cela signifie que dans C++11 et les versions ultérieures, l'utilisation du mot-clé register n'aura aucun effet sur le programme.
En C++, les références ou les pointeurs peuvent être utilisés pour améliorer la vitesse d'accès, en particulier lorsqu'il s'agit de grandes structures de données.
classe de stockage static
staticLa classe de stockage indique au compilateur de maintenir les variables locales en vie pendant toute la durée de vie du programme, sans avoir besoin de les créer et de les détruire chaque fois qu'elles entrent et sortent de la portée. Par conséquent, utiliser static pour modifier une variable locale peut préserver la valeur de la variable locale entre les appels de fonction.
Le modificateur static peut également être appliqué à des variables globales. Lorsque static modifie une variable globale, il limite la portée de la variable au fichier dans lequel elle est déclarée.
En C++, lorsque static est utilisé sur un membre de données de classe, il en résulte qu'une seule copie de ce membre est partagée par tous les objets de la classe.
Exemple
Lorsque le code ci-dessus est compilé et exécuté, il produit les résultats suivants :
变量 i 为 6 , 变量 count 为 9 变量 i 为 7 , 变量 count 为 8 变量 i 为 8 , 变量 count 为 7 变量 i 为 9 , 变量 count 为 6 变量 i 为 10 , 变量 count 为 5 变量 i 为 11 , 变量 count 为 4 变量 i 为 12 , 变量 count 为 3 变量 i 为 13 , 变量 count 为 2 变量 i 为 14 , 变量 count 为 1 变量 i 为 15 , 变量 count 为 0
classe de stockage extern
externLa classe de stockage est utilisée pour fournir une référence à une variable globale qui est visible par tous les fichiers du programme. Lorsque vous utilisez 'extern' pour une variable qui ne peut pas être initialisée, il fait pointer le nom de la variable vers un emplacement de stockage précédemment défini.
Lorsque vous avez plusieurs fichiers et que vous définissez une variable globale ou une fonction qui peut être utilisée dans d'autres fichiers, vous pouvez l'utiliser dans d'autres fichiers.externpour obtenir une référence à la variable ou à la fonction définie. Cela peut être compris ainsi,externIl est utilisé pour déclarer une variable globale ou une fonction dans un autre fichier.
Le modificateur extern est généralement utilisé lorsque deux fichiers ou plus partagent les mêmes variables globales ou fonctions, comme montré ci-dessous :
Premier fichier : main.cpp
Exemple
Deuxième fichier : support.cpp
Exemple
Ici, dans le second fichier, le mot-cléexternest utilisé pour déclarer count, qui a déjà été défini dans le premier fichier main.cpp. Maintenant, compilez ces deux fichiers comme suit :
$ g++ main.cpp support.cpp -o write
Cela produira le programme exécutablewrite, essayez d'exécuterwrite, cela produira les résultats suivants :
$ ./write Count is 5
classe de stockage mutable
mutable est un mot-clé utilisé pour modifier les variables membres de classe afin qu'elles puissent être modifiées dans des fonctions membres const. Normalement, les fonctions membres const ne peuvent pas modifier l'état d'un objet, mais si une variable membre est déclarée mutable, elle peut être modifiée dans des fonctions const.
Fonctionnalités :
- Permet la modification:
mutableles variables membres peuvent êtreconstla fonction membre a été modifiée. - Objectif de conception: Il est généralement utilisé dans des scénarios qui nécessitent une gestion d'état sans modifier l'état externe d'un objet, comme la mise en cache, l'évaluation paresseuse, etc.
Exemple
class Example {
public:
Example() : value(0), cachedValue(0) {}
// fonction membre const
int getValue() const {
return value; // Lire le membre const
}
// Modifier le membre mutable
void increment() {
++value;
cachedValue = value * 2; // Modifier le membre mutable
}
int getCachedValue() const {
return cachedValue; // Lire le membre mutable
}
private:
int value; // Membre normal, ne peut pas être modifié dans une fonction const
mutable int cachedValue; // Membre mutable, peut être modifié dans les fonctions const
};
int main() {
const Example ex;
// ex.increment(); // Erreur : impossible d'appeler une fonction non const sur un objet const
// ex.value = 10; // Erreur : impossible de modifier un membre d'un objet const
std::cout << "Valeur : " << ex.getValue() << std::endl;
std::cout << "Valeur en cache : " << ex.getCachedValue() << std::endl; // La sortie est 0
return 0;
}
Scénarios d'application :
- Mise en cache: dans
constCalculer et mettre en cache le résultat dans la fonction sans affecter l'état externe de l'objet. - Suivi d'état: Par exemple, compteurs de logs, informations de suivi telles que le nombre d'appels, en évitant des modifications intrusives de la logique de classe.
Notes :
mutableIl convient d'être prudent lors de l'utilisation de la variable afin d'éviter des changements d'état inattendus, ce qui peut nuire à la lisibilité et à la maintenabilité du code.mutables'applique à la nécessité deconstIl s'agit d'un cas spécifique de modification de l'état dans l'environnement, plutôt que d'un modèle de conception universel.
classe de stockage thread_local
thread_local est une classe de stockage introduite en C++11, utilisée pour gérer des variables spécifiques à un thread dans un environnement multithread.
Les variables modifiées avec thread_local ont des instances indépendantes dans chaque thread, de sorte que les opérations de chaque thread sur la variable n'affectent pas les autres threads.
- Indépendance: Chaque thread possède sa propre copie indépendante de la variable, et les opérations de lecture/écriture entre différents threads n'interfèrent pas les unes avec les autres.
- Cycle de vie:
thread_localLa variable est automatiquement détruite lorsque son thread se termine. - Initialiser:
thread_localLes variables peuvent être initialisées de manière statique ou dynamique, et prennent en charge l'initialisation au moment de la déclaration.
thread_local convient aux scénarios qui nécessitent de stocker l'état d'un thread, des caches, ou d'éviter les courses de données, tels que les pools de threads, les contextes de requête, etc.
Ce qui suit illustre des variables qui peuvent être déclarées comme thread_local :
Notes :
- Performance: Comme chaque thread a une copie indépendante,
thread_localLa vitesse d'accès à la variable peut être légèrement plus lente que celle des variables globales ou statiques. - Stockage statique:
thread_localLe type de stockage de la variable est une durée de stockage statique, elle persiste donc pendant toute la durée d'exécution du programme.