const static int foo = 42;
J'ai vu cela dans certains codes ici sur StackOverflow et je ne pouvais pas comprendre ce que ça faisait. Ensuite, j'ai vu des réponses confuses sur d'autres forums. Ma meilleure hypothèse est qu'il est utilisé en C pour masquer la constante foo
des autres modules. Est-ce correct? Si tel est le cas, pourquoi l’utiliserait-il dans un contexte C++ où vous pouvez simplement le rendre private
?
Il a des utilisations à la fois en C et en C++.
Comme vous l'avez deviné, la partie static
limite sa portée à celle-ci nité de compilation . Il prévoit également une initialisation statique. const
indique simplement au compilateur de ne laisser personne le modifier. Cette variable est soit placée dans le segment data ou bss selon l'architecture, et peut être en mémoire marquée en lecture seule.
C’est ainsi que C traite ces variables (ou comment C++ traite les variables d’espace de nommage). En C++, un membre marqué static
est partagé par toutes les instances d'une classe donnée. Qu'elle soit privée ou non n'affecte pas le fait qu'une variable est partagée par plusieurs instances. Avoir const
là vous avertira si un code tente de le modifier.
S'il était strictement privé, chaque instance de la classe obtiendrait sa propre version (nonobstant l'optimiseur).
Beaucoup de gens ont donné la réponse de base, mais personne n'a signalé qu'en C++, const
par défaut était static
au niveau namespace
(et certains ont donné des informations erronées). Voir la section 3.5.3 du standard C++ 98.
D'abord un fond:
Unité de traduction: Un fichier source après le pré-processeur (de manière récursive) incluait tous ses fichiers d'inclusion.
Liaison statique: Un symbole est uniquement disponible dans son unité de traduction.
Lien externe: Un symbole est disponible à partir d'autres unités de traduction.
namespace
Ceci inclut l'espace de noms global, autrement dit les variables globales .
static const int sci = 0; // sci is explicitly static
const int ci = 1; // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3; // ei is explicitly extern
int i = 4; // i is implicitly extern
static int si = 5; // si is explicitly static
static
signifie que la valeur est conservée entre les appels de fonction.
La sémantique de la fonction static
variables est similaire à celle des variables globales en ce sens qu'elles résident dans le segment de données du programme (et non la pile ou le tas), voir - cette question pour plus de détails sur la durée de vie de static
variables.
class
static
signifie que la valeur est partagée entre toutes les instances de la classe et const
signifie qu'elle ne change pas.
Cette ligne de code peut en fait apparaître dans plusieurs contextes différents. Même si son comportement est à peu près identique, il existe de petites différences.
// foo.h
static const int i = 0;
'i
' sera visible dans chaque unité de traduction incluant l'en-tête. Cependant, à moins que vous n'utilisiez réellement l'adresse de l'objet (par exemple. '&i
'), Je suis à peu près sûr que le compilateur traitera 'i
' simplement comme un type safe 0
. Lorsque deux autres unités de traduction prennent le '&i
', L'adresse sera différente pour chaque unité de traduction.
// foo.cc
static const int i = 0;
'i
' a un lien interne et ne peut donc pas être consulté de l'extérieur de cette unité de traduction. Cependant, encore une fois, à moins que vous n'utilisiez son adresse, elle sera probablement traitée comme un type sûr 0
.
Une chose à noter est la déclaration suivante:
const int i1 = 0;
est exactement identique à static const int i = 0
. Une variable dans un espace de noms déclaré avec const
et non explicitement déclarée avec extern
est implicitement statique. Si vous y réfléchissez, le comité C++ avait l'intention d'autoriser la déclaration des variables const
dans les fichiers d'en-tête sans toujours avoir besoin du mot clé static
pour ne pas rompre l'ODR.
class A {
public:
static const int i = 0;
};
Dans l'exemple ci-dessus, la norme spécifie explicitement qu'il n'est pas nécessaire de définir 'i
' si son adresse n'est pas requise. En d'autres termes, si vous utilisez uniquement 'i
' en tant que type 0, le compilateur ne le définira pas. Une différence entre les versions de classe et d’espace de noms est que l’adresse de 'i
' (si elle est utilisée dans deux unités de traduction ou plus) sera la même pour le membre de la classe. Lorsque l'adresse est utilisée, vous devez avoir une définition pour cela:
// a.h
class A {
public:
static const int i = 0;
};
// a.cc
#include "a.h"
const int A::i; // Definition so that we can take the address
C'est une optimisation de petit espace.
Quand tu dis
const int foo = 42;
Vous ne définissez pas une constante, mais créez une variable en lecture seule. Le compilateur est assez intelligent pour utiliser 42 chaque fois qu'il voit foo, mais il allouera également de l'espace dans la zone de données initialisée. Ceci est fait parce que, comme défini, foo a un lien externe. Une autre unité de compilation peut dire:
extern externe int foo;
Pour avoir accès à sa valeur. Ce n'est pas une bonne pratique car cette unité de compilation n'a aucune idée de la valeur de foo. Il sait juste que c'est un int entier et doit recharger la valeur de la mémoire chaque fois qu'il est utilisé.
Maintenant, en déclarant que c'est statique:
static const int foo = 42;
Le compilateur peut faire son optimisation habituelle, mais il peut aussi dire "hé, personne en dehors de cette unité de compilation ne peut voir foo et je sais qu'il est toujours 42 donc il n'est pas nécessaire de lui allouer de la place".
Je devrais également noter qu'en C++, le moyen préféré d'empêcher les noms d'échapper à l'unité de compilation en cours consiste à utiliser un espace de noms anonyme:
namespace {
const int foo = 42; // same as static definition above
}
Il manque un 'int'. CA devrait etre:
const static int foo = 42;
En C et C++, il déclare une constante entière avec une étendue de fichier local de valeur 42.
Pourquoi 42? Si vous ne le savez pas déjà (et que vous avez du mal à le croire), c’est une référence à la réponse à la vie, à l’univers et à tout .
En C++,
static const int foo = 42;
est le moyen préféré pour définir et utiliser des constantes. C'est à dire. utiliser ceci plutôt que
#define foo 42
parce qu'il ne subvertit pas le système de sécurité de type.
Pour toutes les bonnes réponses, je veux ajouter un petit détail:
Si vous écrivez des plugins (par exemple, des DLL ou des bibliothèques .so à charger par un système CAD)), alors statique est un économiseur de vie évitant les conflits de noms, comme celui-ci:
Pire encore: l'étape 3 peut se comporter différemment selon l'optimisation du compilateur, le mécanisme de chargement du plug-in, etc.
J'ai eu ce problème une fois avec deux fonctions d'assistance (même nom, comportement différent) dans deux plugins. Les déclarer statiques a résolu le problème.
Selon les spécifications C99/GNU99:
static
est spécificateur de classe de stockage
les objets de la portée du niveau fichier ont par défaut une liaison externe
const
est un qualificatif de type (fait partie du type)
mot-clé appliqué à l'instance de gauche immédiate - c'est-à-dire.
MyObj const * myVar;
- pointeur non qualifié sur le type d'objet qualifié const
MyObj * const myVar;
- pointeur qualifié const vers un type d'objet non qualifié
Utilisation la plus à gauche - appliquée au type d'objet, pas variable
const MyObj * myVar;
- pointeur non qualifié sur le type d'objet qualifié constTHUS:
static NSString * const myVar;
- pointeur constant sur une chaîne immuable avec une liaison interne.
L'absence du mot clé static
rendra le nom de la variable global et pourrait entraîner des conflits de noms au sein de l'application.
Oui, il cache une variable dans un module à partir d'autres modules. En C++, je l'utilise quand je ne veux pas/n'ai pas besoin de changer un fichier .h qui déclenchera une reconstruction inutile d'autres fichiers. Aussi, je mets d'abord la statique:
static const int foo = 42;
De plus, selon son utilisation, le compilateur ne lui allouera même pas de stockage et se contente de "mettre en ligne" la valeur à laquelle il est utilisé. Sans la statique, le compilateur ne peut pas supposer qu'il n'est pas utilisé ailleurs ni en ligne.
Cette constante globale est visible/accessible uniquement dans le module de compilation (fichier .cpp). BTW utilisant statique à cette fin est obsolète. Mieux vaut utiliser un espace de noms anonyme et une énumération:
namespace
{
enum
{
foo = 42
};
}
Le rendre privé signifierait toujours qu'il apparaît dans l'en-tête. J'ai tendance à utiliser "le plus faible" moyen qui fonctionne. Voir cet article classique de Scott Meyers: http://www.ddj.com/cpp/184401197 (il concerne les fonctions, mais peut également être appliqué ici).
C++ 17 inline
variables
Si vous recherchez "une constante statique C++" dans Googled, c'est très probablement ce que vous voulez vraiment utiliser: variables en ligne C++ 17 .
Cette fonctionnalité géniale de C++ 17 nous permet de:
constexpr
: Comment déclarer constexpr extern?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Compiler et exécuter:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
Voir aussi: Comment fonctionnent les variables en ligne?
Norme C++ sur les variables en ligne
La norme C++ garantit que les adresses seront les mêmes. projet de norme C++ 17 N4659 10.1.6 "Le spécificateur en ligne":
6 Une fonction ou une variable en ligne avec une liaison externe doit avoir la même adresse dans toutes les unités de traduction.
cppreference https://en.cppreference.com/w/cpp/language/inline explique que si static
n'est pas indiqué, il est associé à un lien externe.
Implémentation de la variable en ligne GCC
Nous pouvons observer comment cela est implémenté avec:
nm main.o notmain.o
qui contient:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
et man nm
dit à propos de u
:
"u" Le symbole est un symbole global unique. Il s’agit d’une extension GNU à l’ensemble standard de liaisons de symboles ELF. Pour un tel symbole, l’éditeur de liens dynamique s’assurera qu’un seul symbole porte ce nom et ce type utilisés dans le processus. .
nous voyons donc qu’il existe une extension ELF dédiée à cela.
Pré-C++ 17: extern const
Avant C++ 17, et en C, nous pouvons obtenir un effet très similaire avec un extern const
, ce qui entraînera l’utilisation d’un seul emplacement mémoire.
Les inconvénients de inline
sont les suivants:
constexpr
avec cette technique, seul inline
permet que: Comment déclarer constexpr extern?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
En-tête pré-C++ 17 uniquement alternatives
Celles-ci ne sont pas aussi bonnes que la solution extern
, mais elles fonctionnent et n'occupent qu'un seul emplacement mémoire:
Une fonction constexpr
, car constexpr
implique inline
et inline
permet à la définition d'apparaître chaque unité de traduction :
constexpr int shared_inline_constexpr() { return 42; }
et je parie que tout compilateur décent s'inscrira dans l'appel.
Vous pouvez également utiliser une variable statique const
ou constexpr
comme dans:
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
mais vous ne pouvez pas prendre des choses comme prendre son adresse ou sinon il devient odr-utilisé, voir aussi: Définition des membres de données statiques constexpr
[~ # ~] c [~ # ~]
En C, la situation est la même que celle de C++ pré C++ 17, j'ai téléchargé un exemple à l'adresse suivante: Que signifie "statique" en C?
La seule différence est qu'en C++, const
implique static
pour les globals, mais pas dans C: sémantique C++ de `static const` vs` const`
Y at-il un moyen de l’intégrer complètement?
TODO: y a-t-il un moyen de complètement intégrer la variable, sans utiliser de mémoire du tout?
Cela ressemble beaucoup à ce que fait le pré-processeur.
Cela nécessiterait en quelque sorte:
Apparenté, relié, connexe:
Testé sous Ubuntu 18.10, GCC 8.2.0.