Recherche…


Introduction

Les spécificateurs de classe de stockage sont des mots clés pouvant être utilisés dans les déclarations. Ils n'affectent pas le type de déclaration, mais modifient généralement la manière dont l'entité est stockée.

Remarques

Il existe six spécificateurs de classe de stockage, mais pas tous dans la même version du langage: auto (jusqu'à C ++ 11), register (jusqu'à C ++ 17), static , thread_local (depuis C ++ 11), extern et mutable .

Selon la norme,

Au plus, un spécificateur de classe de stockage doit apparaître dans une déclaration-spécificateur-seq donnée , sauf que thread_local peut apparaître avec static ou extern .

Une déclaration ne peut contenir aucun spécificateur de classe de stockage. Dans ce cas, le langage spécifie un comportement par défaut. Par exemple, par défaut, une variable déclarée à la portée du bloc a implicitement une durée de stockage automatique.

mutable

Un spécificateur qui peut être appliqué à la déclaration d'un membre de données non statique et non référencé d'une classe. Un membre mutable d'une classe n'est pas const quand l'objet est const .

class C {
    int x;
    mutable int times_accessed;
  public:
    C(): x(0), times_accessed(0) {
    }
    int get_x() const {
        ++times_accessed; // ok: const member function can modify mutable data member
        return x;
    }
    void set_x(int x) {
        ++times_accessed;
        this->x = x;
    }
};
C ++ 11

Une deuxième signification pour mutable été ajoutée en C ++ 11. Quand il suit la liste de paramètres d'un lambda, il supprime le const implicite sur l'opérateur d'appel de la fonction de lambda. Par conséquent, un lambda mutable peut modifier les valeurs des entités capturées par copie. Voir lambda mutable pour plus de détails.

std::vector<int> my_iota(int start, int count) {
    std::vector<int> result(count);
    std::generate(result.begin(), result.end(),
                  [start]() mutable { return start++; });
    return result;
}

Notez que mutable n'est pas un spécificateur de classe de stockage lorsqu'il est utilisé de cette manière pour former un lambda modifiable.

registre

C ++ 17

Un spécificateur de classe de stockage qui indique au compilateur qu'une variable sera fortement utilisée. Le mot "register" est lié au fait qu'un compilateur pourrait choisir de stocker une telle variable dans un registre de CPU afin de pouvoir y accéder en moins de cycles d'horloge. Il était obsolète en C ++ 11.

register int i = 0;
while (i < 100) {
    f(i);
    int g = i*i;
    i += h(i, g);
}

Les variables locales et les paramètres de fonction peuvent être déclarés register . Contrairement à C, C ++ ne place aucune restriction sur ce qui peut être fait avec une variable de register . Par exemple, il est valable de prendre l'adresse d'une variable de register , mais cela peut empêcher le compilateur de stocker réellement une telle variable dans un registre.

C ++ 17

Le register mots clés est inutilisé et réservé. Un programme qui utilise le register mots-clés est mal formé.

statique

Le spécificateur de classe de stockage static a trois significations différentes.

  1. Donne un lien interne à une variable ou une fonction déclarée dans la portée de l'espace de noms.

    // internal function; can't be linked to
    static double semiperimeter(double a, double b, double c) {
        return (a + b + c)/2.0;
    }
    // exported to client
    double area(double a, double b, double c) {
        const double s = semiperimeter(a, b, c);
        return sqrt(s*(s-a)*(s-b)*(s-c));
    }
    
  2. Déclare une variable pour avoir une durée de stockage statique (sauf s'il s'agit de thread_local ). Les variables d'espace de nommage sont implicitement statiques. Une variable locale statique est initialisée une seule fois, la première fois que le contrôle passe par sa définition et n'est pas détruite à chaque fois que son étendue est supprimée.

    void f() {
        static int count = 0;
        std::cout << "f has been called " << ++count << " times so far\n";
    }
    
  3. Appliqué à la déclaration d'un membre de la classe, déclare que ce membre est un membre statique .

    struct S {
        static S* create() {
            return new S;
        }
    };
    int main() {
        S* s = S::create();
    }
    

Notez que dans le cas d'un membre de données statique d'une classe, 2 et 3 s'appliquent simultanément: le mot clé static transforme le membre en un membre de données statique et en fait une variable avec une durée de stockage statique.

auto

C ++ 03

Déclare une variable pour avoir une durée de stockage automatique. Il est redondant, car la durée de stockage automatique est déjà la valeur par défaut pour la portée du bloc et le spécificateur automatique n'est pas autorisé pour la portée de l'espace de noms.

void f() {
    auto int x; // equivalent to: int x;
    auto y;     // illegal in C++; legal in C89
}
auto int z;     // illegal: namespace-scope variable cannot be automatic

En C ++ 11, auto signification auto change complètement et n'est plus un spécificateur de classe de stockage, mais est plutôt utilisée pour la déduction de type .

externe

Le spécificateur de classe de stockage extern peut modifier une déclaration de l'une des trois manières suivantes, en fonction du contexte:

  1. Il peut être utilisé pour déclarer une variable sans la définir. En règle générale, cela est utilisé dans un fichier d'en-tête pour une variable qui sera définie dans un fichier de mise en œuvre distinct.

    // global scope
    int x;             // definition; x will be default-initialized
    extern int y;      // declaration; y is defined elsewhere, most likely another TU
    extern int z = 42; // definition; "extern" has no effect here (compiler may warn)
    
  2. Il donne un lien externe à une variable dans la portée de l’espace de noms, même si const ou constexpr aurait autrement eu un lien interne.

    // global scope
    const int w = 42;            // internal linkage in C++; external linkage in C
    static const int x = 42;     // internal linkage in both C++ and C
    extern const int y = 42;     // external linkage in both C++ and C
    namespace {
        extern const int z = 42; // however, this has internal linkage since
                                 // it's in an unnamed namespace
    }
    
  3. Elle redéfinit une variable à portée de bloc si elle a déjà été déclarée avec la liaison. Sinon, il déclare une nouvelle variable avec linkage, qui est un membre de l'espace de nommage le plus proche.

    // global scope
    namespace {
        int x = 1;
        struct C {
            int x = 2;
            void f() {
                extern int x;           // redeclares namespace-scope x
                std::cout << x << '\n'; // therefore, this prints 1, not 2
            }
        };
    }
    void g() {
        extern int y; // y has external linkage; refers to global y defined elsewhere
    }
    

Une fonction peut également être déclarée extern , mais cela n'a aucun effet. Il est généralement utilisé comme un indice pour le lecteur qu'une fonction déclarée ici est définie dans une autre unité de traduction. Par exemple:

 void f();        // typically a forward declaration; f defined later in this TU
 extern void g(); // typically not a forward declaration; g defined in another TU

Dans le code ci-dessus, si f était changé en extern et g en non- extern , cela n'affecterait pas du tout l'exactitude ou la sémantique du programme, mais pourrait induire le lecteur en erreur.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow