C++
Specifiers di classe di archiviazione
Ricerca…
introduzione
Gli identificatori delle classi di archiviazione sono parole chiave che possono essere utilizzate nelle dichiarazioni. Non influenzano il tipo della dichiarazione, ma in genere modificano il modo in cui l'entità è archiviata.
Osservazioni
Esistono sei identificatori di classe di memoria, sebbene non tutti nella stessa versione della lingua: auto
(fino a C ++ 11), register
(fino a C ++ 17), static
, thread_local
(dal C ++ 11), extern
e mutable
.
Secondo lo standard,
Al massimo un identificatore della classe di memoria deve apparire in un declinatore-se-specificatore seq, eccetto che
thread_local
può apparire constatic
oextern
.
Una dichiarazione potrebbe non contenere alcun identificatore di classe di memoria. In tal caso, la lingua specifica un comportamento predefinito. Ad esempio, per impostazione predefinita, una variabile dichiarata a livello di blocco ha implicitamente durata di archiviazione automatica.
mutevole
Un identificatore che può essere applicato alla dichiarazione di un membro dati non statico e non di riferimento di una classe. Un membro mutabile di una classe non è const
nemmeno quando l'oggetto è 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;
}
};
Un secondo significato per mutable
stato aggiunto in C ++ 11. Quando segue la lista dei parametri di un lambda, sopprime il const
implicito sull'operatore di chiamata della funzione lambda. Pertanto, un lambda mutabile può modificare i valori delle entità catturate dalla copia. Vedi mutable lambda per maggiori dettagli.
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;
}
Si noti che mutable
non è uno specificatore della classe di memoria quando viene usato in questo modo per formare un lambda mutabile.
Registrare
Un identificatore della classe di memoria che suggerisce al compilatore che una variabile sarà usata pesantemente. La parola "registro" è legata al fatto che un compilatore potrebbe scegliere di memorizzare tale variabile in un registro della CPU in modo che sia accessibile in un numero inferiore di cicli di clock. È stato deprecato a partire da C ++ 11.
register int i = 0;
while (i < 100) {
f(i);
int g = i*i;
i += h(i, g);
}
Sia le variabili locali che i parametri di funzione possono essere dichiarati register
. A differenza di C, C ++ non pone alcuna restrizione su cosa può essere fatto con una variabile di register
. Ad esempio, è valido prendere l'indirizzo di una variabile di register
, ma ciò potrebbe impedire al compilatore di memorizzare effettivamente tale variabile in un registro.
Il register
parole chiave non è utilizzato e riservato. Un programma che utilizza il register
parole chiave è mal formato.
statico
Lo specificatore della classe di archiviazione static
ha tre diversi significati.
Fornisce il collegamento interno a una variabile o funzione dichiarata nell'ambito dello spazio dei nomi.
// 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)); }
Dichiara una variabile con durata di memorizzazione statica (a meno che non sia
thread_local
). Le variabili nell'ambito dello spazio dei nomi sono implicitamente statiche. Una variabile locale statica viene inizializzata solo una volta, il primo controllo del tempo passa attraverso la sua definizione e non viene distrutta ogni volta che si esce dall'ambito.void f() { static int count = 0; std::cout << "f has been called " << ++count << " times so far\n"; }
Quando viene applicato alla dichiarazione di un membro della classe, dichiara che il membro deve essere un membro statico .
struct S { static S* create() { return new S; } }; int main() { S* s = S::create(); }
Si noti che nel caso di un membro di dati statici di una classe, entrambi 2 e 3 si applicano contemporaneamente: la parola chiave static
fa sia il membro in un membro di dati statici che lo trasforma in una variabile con durata di archiviazione statica.
auto
Dichiara una variabile con durata della memorizzazione automatica. È ridondante, poiché la durata della memorizzazione automatica è già l'impostazione predefinita nell'ambito del blocco e l'identificatore automatico non è consentito nello spazio dei nomi.
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
In C ++ 11, l' auto
cambiato completamente il significato e non è più un identificatore della classe di memoria, ma viene invece utilizzato per la deduzione del tipo .
extern
L' extern
classe di archiviazione extern
può modificare una dichiarazione in uno dei tre modi seguenti, a seconda del contesto:
Può essere usato per dichiarare una variabile senza definirla. In genere, questo viene utilizzato in un file di intestazione per una variabile che verrà definita in un file di implementazione separato.
// 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)
Fornisce il collegamento esterno a una variabile in ambito namespace anche se
const
oconstexpr
avrebbero altrimenti causato un collegamento interno.// 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 }
Ricalcola una variabile a livello di blocco se precedentemente dichiarata con linkage. Altrimenti, dichiara una nuova variabile con linkage, che è un membro del più vicino spazio dei nomi.
// 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 }
Una funzione può anche essere dichiarata extern
, ma ciò non ha alcun effetto. Solitamente viene usato come suggerimento per il lettore che una funzione dichiarata qui è definita in un'altra unità di traduzione. Per esempio:
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
Nel codice precedente, se f
stato cambiato in extern
e g
in non- extern
, non influenzerebbe affatto la correttezza o la semantica del programma, ma probabilmente confonderebbe il lettore del codice.