C++
Speicherklassenspezifizierer
Suche…
Einführung
Speicherklassenspezifizierer sind Schlüsselwörter , die in Deklarationen verwendet werden können. Sie wirken sich nicht auf den Typ der Deklaration aus, ändern jedoch normalerweise die Art und Weise, in der die Entität gespeichert wird.
Bemerkungen
Es gibt sechs Speicherklassenspezifizierer, allerdings nicht alle in derselben Sprachversion: auto
(bis C ++ 11), register
(bis C ++ 17), static
, thread_local
(seit C ++ 11), extern
und mutable
.
Nach dem Standard
Es darf höchstens ein Speicherklassenspezifizierer in einem angegebenen
thread_local
-seq vorkommen, mit der Ausnahme, dassthread_local
static
oderextern
.
Eine Deklaration darf keinen Speicherklassenspezifizierer enthalten. In diesem Fall gibt die Sprache ein Standardverhalten an. Standardmäßig hat eine im Blockbereich deklarierte Variable implizit eine automatische Speicherdauer.
veränderlich
Ein Bezeichner, der auf die Deklaration eines nicht statischen Nicht-Referenzdatenelements einer Klasse angewendet werden kann. Ein veränderlicher Member einer Klasse ist nicht const
selbst wenn das Objekt 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;
}
};
Eine zweite Bedeutung für mutable
wurde in C ++ 11 hinzugefügt. Wenn es der Parameterliste eines Lambda folgt, unterdrückt es die implizite const
Anweisung des Funktionsaufrufoperators des Lambda. Daher kann ein veränderliches Lambda die Werte von durch Kopie erfassten Entitäten ändern. Weitere Informationen finden Sie in den veränderbaren Lambdas .
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;
}
Beachten Sie, dass mutable
kein Speicherklassenspezifizierer ist, wenn auf diese Weise ein mutierbares Lambda gebildet wird.
registrieren
Ein Speicherklassenspezifizierer, der den Compiler darauf hinweist, dass eine Variable stark verwendet wird. Das Wort "Register" bezieht sich auf die Tatsache, dass ein Compiler eine solche Variable in einem CPU-Register speichern kann, so dass in weniger Taktzyklen darauf zugegriffen werden kann. Es wurde ab C ++ 11 veraltet.
register int i = 0;
while (i < 100) {
f(i);
int g = i*i;
i += h(i, g);
}
Sowohl lokale Variablen als auch Funktionsparameter können als register
deklariert register
. Im Gegensatz zu C gibt C ++ keine Einschränkungen vor, was mit einer register
ist. Es ist beispielsweise gültig, die Adresse einer register
zu übernehmen, dies kann jedoch verhindern, dass der Compiler eine solche Variable tatsächlich in einem Register speichert.
Das Schlüsselwort - register
wird nicht verwendet und reserviert. Ein Programm, das das Schlüsselwortregister verwendet register
ist schlecht geformt.
statisch
Der static
Speicherklassenspezifizierer hat drei verschiedene Bedeutungen.
Gibt eine interne Verknüpfung zu einer im Namespace-Bereich deklarierten Variablen oder Funktion.
// 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)); }
Deklariert eine Variable mit statischer Speicherdauer (sofern es sich nicht um
thread_local
). Namespace-Gültigkeitsbereichsvariablen sind implizit statisch. Eine statische lokale Variable wird nur einmal initialisiert. Das erste Mal, wenn die Steuerung ihre Definition durchläuft, wird nicht bei jedem Verlassen des Gültigkeitsbereichs zerstört.void f() { static int count = 0; std::cout << "f has been called " << ++count << " times so far\n"; }
Wenn es auf die Deklaration eines Klassenmitglieds angewendet wird, wird dieses Mitglied als statisches Mitglied deklariert.
struct S { static S* create() { return new S; } }; int main() { S* s = S::create(); }
Beachten Sie, dass im Falle eines statischen Datenelements einer Klasse sowohl 2 als auch 3 gleichzeitig gelten: Das static
Schlüsselwort macht das Member zu einem statischen Datenmitglied und zu einer Variablen mit statischer Speicherdauer.
Auto
Deklariert eine Variable für die automatische Speicherdauer. Sie ist redundant, da die automatische Speicherdauer im Blockbereich bereits Standard ist und der automatische Bezeichner im Namespace-Bereich nicht zulässig ist.
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 hat auto
die Bedeutung komplett geändert und ist nicht länger ein Speicherklassenspezifizierer, sondern wird stattdessen für die Typabzugung verwendet .
extern
Der extern
Speicherklassenspezifizierer kann eine Deklaration je nach Kontext auf eine der drei folgenden Arten ändern:
Es kann verwendet werden, um eine Variable zu deklarieren, ohne sie zu definieren. Normalerweise wird dies in einer Headerdatei für eine Variable verwendet, die in einer separaten Implementierungsdatei definiert wird.
// 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)
Es gibt eine externe Verknüpfung zu einer Variablen im Gültigkeitsbereich des Namespaces, auch wenn
const
oderconstexpr
die interne Verknüpfung sonst verursacht hätte.// 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 }
Es deklariert eine Variable im Blockbereich neu, wenn sie zuvor mit Verknüpfung deklariert wurde. Andernfalls wird eine neue Variable mit Verknüpfung deklariert, die dem nächstgelegenen umschließenden Namespace angehört.
// 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 }
Eine Funktion kann auch extern
deklariert werden, dies hat jedoch keine Auswirkungen. Sie wird normalerweise als Hinweis für den Leser verwendet, dass eine hier deklarierte Funktion in einer anderen Übersetzungseinheit definiert ist. Zum Beispiel:
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
Wenn im obigen Code f
auf extern
und g
auf nicht extern
geändert wurde, würde dies die Korrektheit oder Semantik des Programms nicht beeinträchtigen, würde aber den Leser des Codes wahrscheinlich verwirren.