C++
Specificaties opslagklasse
Zoeken…
Invoering
Opslagklasse-aanduidingen zijn trefwoorden die in aangiften kunnen worden gebruikt. Ze hebben geen invloed op het type aangifte, maar wijzigen doorgaans de manier waarop de entiteit wordt opgeslagen.
Opmerkingen
Er zijn zes opslagklasse-aanduidingen, hoewel niet alle in dezelfde taalversie: auto
(tot C ++ 11), register
(tot C ++ 17), static
, thread_local
(sinds C ++ 11), extern
en mutable
.
Volgens de norm
Er zal hoogstens één opslagklasse-specificatie verschijnen in een gegeven DEC-specificatie-seq, behalve dat
thread_local
kan verschijnen metstatic
ofextern
.
Een aangifte mag geen specificatie voor opslagklasse bevatten. In dat geval geeft de taal een standaardgedrag aan. Standaard heeft bijvoorbeeld een variabele die bij blokbereik wordt gedeclareerd impliciet automatische opslagduur.
veranderlijk
Een specificatie die kan worden toegepast op de aangifte van een niet-statisch, niet-referentiegegevenslid van een klasse. Een veranderlijk lid van een klasse is geen const
zelfs niet als het object 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;
}
};
Een tweede betekenis voor mutable
is toegevoegd in C ++ 11. Wanneer het de parameterlijst van een lambda volgt, onderdrukt het de impliciete const
op de functieoproepoperator van de lambda. Daarom kan een veranderlijke lambda de waarden van entiteiten die zijn vastgelegd door kopiëren wijzigen. Zie veranderlijke lambdas voor meer informatie.
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;
}
Merk op dat mutable
geen specificatie van opslagklasse is wanneer het op deze manier wordt gebruikt om een mutable lambda te vormen.
registreren
Een opslagklasse-aanduiding die de compiler aangeeft dat een variabele intensief zal worden gebruikt. Het woord "register" houdt verband met het feit dat een compiler ervoor zou kunnen kiezen om een dergelijke variabele in een CPU-register op te slaan, zodat deze in minder klokcycli toegankelijk is. Het werd afgeschaft vanaf C ++ 11.
register int i = 0;
while (i < 100) {
f(i);
int g = i*i;
i += h(i, g);
}
Zowel lokale variabelen als functieparameters kunnen register
worden verklaard. In tegenstelling tot C, legt C ++ geen beperkingen op aan wat kan worden gedaan met een register
. Het is bijvoorbeeld geldig om het adres van een register
te nemen, maar dit kan voorkomen dat de compiler zo'n variabele daadwerkelijk in een register opslaat.
Het sleutelwoord register
ongebruikt en gereserveerd. Een programma dat het zoekwoord gebruikt register
is slecht gevormd.
statisch
De static
opslagklasse-aanduiding heeft drie verschillende betekenissen.
Geeft interne koppeling aan een variabele of functie die op naamruimtebereik is gedeclareerd.
// 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)); }
Declareert dat een variabele een statische opslagduur heeft (tenzij het
thread_local
). Variabelen voor naamruimtebereik zijn impliciet statisch. Een statische lokale variabele wordt slechts eenmaal geïnitialiseerd, de eerste tijdcontrole doorloopt zijn definitie en wordt niet vernietigd telkens wanneer de scope wordt verlaten.void f() { static int count = 0; std::cout << "f has been called " << ++count << " times so far\n"; }
Wanneer toegepast op de verklaring van een lid van de klasse, verklaart dat lid een statisch lid .
struct S { static S* create() { return new S; } }; int main() { S* s = S::create(); }
Merk op dat in het geval van een lid met statische gegevens van een klasse, zowel 2 als 3 tegelijkertijd van toepassing zijn: het static
trefwoord maakt van het lid zowel een statisch gegevenslid als een variabele met een statische opslagduur.
auto
Declareert een variabele om automatische opslagduur te hebben. Het is overbodig, omdat automatische opslagduur al de standaard is bij block scope en de auto specifier niet is toegestaan bij namespace scope.
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 heeft auto
de betekenis volledig gewijzigd en is niet langer een specificatie voor de opslagklasse, maar wordt in plaats daarvan gebruikt voor het aftrekken van het type .
extern
De extern
opslagklasse- extern
kan een aangifte op een van de volgende drie manieren wijzigen, afhankelijk van de context:
Het kan worden gebruikt om een variabele te declareren zonder deze te definiëren. Meestal wordt dit gebruikt in een headerbestand voor een variabele die wordt gedefinieerd in een afzonderlijk implementatiebestand.
// 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)
Het geeft externe koppeling aan een variabele op naamruimtebereik, zelfs als
const
ofconstexpr
anders een interne koppeling zou hebben veroorzaakt.// 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 }
Het herdefinieert een variabele op blokbereik als deze eerder met koppeling werd gedeclareerd. Anders declareert het een nieuwe variabele met koppeling, die lid is van de dichtstbijzijnde omringende naamruimte.
// 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 }
Een functie kan ook extern
worden verklaard, maar dit heeft geen effect. Het wordt meestal gebruikt als een hint voor de lezer dat een hier aangegeven functie is gedefinieerd in een andere vertaaleenheid. Bijvoorbeeld:
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
In de bovenstaande code, als f
zou worden gewijzigd in extern
en g
in niet- extern
, zou dit de correctheid of semantiek van het programma helemaal niet beïnvloeden, maar zou het waarschijnlijk de lezer van de code verwarren.