C++
Lagringsklassspecifikationer
Sök…
Introduktion
Lagringsklassspecifikationer är nyckelord som kan användas i deklarationer. De påverkar inte typ av deklaration, men ändrar vanligtvis sättet på vilket enheten lagras.
Anmärkningar
Det finns sex lagringsklassspecifikationer, även om inte alla i samma version av språket: auto
(tills C ++ 11), register
(tills C ++ 17), static
, thread_local
(sedan C ++ 11), extern
och mutable
.
Enligt standarden
Högst en lagerklass-specifikatör ska visas i en given decl-specificier-seq, förutom att
thread_local
kan visas medstatic
ellerextern
.
En deklaration får inte innehålla någon specifikation för lagringsklass. I så fall anger språket ett standardbeteende. Till exempel har en variabel som deklarerats till blockomfång implicit automatisk lagringsvaraktighet.
föränderlig
En specifikator som kan tillämpas på deklarationen av en icke-statisk, icke-referensdatamedlem i en klass. En föränderlig medlem i en klass är inte const
även när objektet är 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;
}
};
En andra betydelse för mutable
tillsattes i C ++ 11. När den följer parameterlistan för en lambda undertrycker den implicit const
på lambdas funktionssamtaloperatör. Därför kan en muterbar lambda modifiera värdena på enheter som fångats med kopia. Se muterbara lambdor för mer information.
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;
}
Observera att mutable
inte är en lagringsklassspecifikator när det används på detta sätt för att bilda en mutbar lambda.
Registrera
En lagringsklassspecifikation som antyder för kompilatorn att en variabel kommer att användas kraftigt. Ordet "register" är relaterat till det faktum att en kompilator kan välja att lagra en sådan variabel i ett CPU-register så att det kan nås i färre klockcykler. Det avskrivs med början i C ++ 11.
register int i = 0;
while (i < 100) {
f(i);
int g = i*i;
i += h(i, g);
}
Både lokala variabler och funktionsparametrar kan förklaras register
. Till skillnad från C sätter C ++ inte några begränsningar för vad som kan göras med en register
. Till exempel är det giltigt att ta adressen till en register
, men detta kan förhindra att kompilatorn faktiskt lagrar en sådan variabel i ett register.
Nyckelordet register
är oanvänd och reserverade. Ett program som använder sökordet register
är illa bildas.
statisk
Den static
lagringsklassspecifikationen har tre olika betydelser.
Ger intern länk till en variabel eller funktion som deklareras inom namnområdet.
// 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)); }
Förklarar att en variabel ska ha statisk lagringsvaraktighet (såvida den inte är
thread_local
). Variabler för namnutrymme är implicit statiska. En statisk lokal variabel initialiseras endast en gång, den första gången kontrollen passerar sin definition och förstörs inte varje gång dess omfattning lämnas.void f() { static int count = 0; std::cout << "f has been called " << ++count << " times so far\n"; }
När den tillämpas på en klassmedlemmars förklaring förklarar den medlemmen att vara en statisk medlem .
struct S { static S* create() { return new S; } }; int main() { S* s = S::create(); }
Observera att när det gäller en statisk datamedlem i en klass gäller både 2 och 3 samtidigt: det static
nyckelordet båda gör medlemmet till ett statiskt datamedlem och gör det till en variabel med statisk lagringsvaraktighet.
bil
Förklarar en variabel för att ha automatisk lagringstid. Det är överflödigt, eftersom automatisk lagringsvaraktighet redan är standard vid blockomfång, och autospecifikationen är inte tillåten i namnområdet.
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
I C ++ 11 ändrade auto
betydelsen fullständigt och är inte längre en specifikation för lagringsklass utan används istället för typavdrag .
extern
Den extern
lagringsklassspecifikationen kan modifiera en deklaration på ett av de tre följande sätten, beroende på sammanhang:
Den kan användas för att deklarera en variabel utan att definiera den. Vanligtvis används detta i en rubrikfil för en variabel som kommer att definieras i en separat implementeringsfil.
// 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)
Det ger extern länk till en variabel i namnutrymmet, även om
const
ellerconstexpr
annars skulle ha orsakat att den har intern länk.// 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 }
Den omklassificerar en variabel vid blockomfång om den tidigare förklarats med koppling. Annars förklarar den en ny variabel med koppling, som är medlem i närmaste bifogade namnutrymme.
// 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 }
En funktion kan också förklaras extern
, men detta har ingen effekt. Det används vanligtvis som ett tips för läsaren att en funktion som deklareras här definieras i en annan översättningsenhet. Till exempel:
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
I koden ovan, om f
ändrades till extern
och g
till icke- extern
, skulle det inte påverka programmets korrekthet eller semantik alls, men det kan troligen förvirra läsaren av koden.