Storage class specifiers
Die Speicherklassenspezifizierer sind ein Teil der decl-specifier-seq der Deklarationssyntax eines Namens. Zusammen mit dem Gültigkeitsbereich des Namens steuern sie zwei unabhängige Eigenschaften des Namens: seine Speicherdauer und seine Bindung .
Inhaltsverzeichnis |
Speicherdauer
Die storage duration ist die Eigenschaft eines Objekts , die die minimale potenzielle Lebensdauer des Speichers definiert, der das Objekt enthält. Die Speicherdauer wird durch das Konstrukt bestimmt, das zur Erstellung des Objekts verwendet wird, und ist eine der folgenden:
- statische Speicherdauer
|
(since C++11) |
- automatische Speicherdauer
- dynamische Speicherdauer
Static , Thread- (seit C++11) und automatische Speicherdauern sind mit Objekten verbunden, die durch Deklarationen eingeführt werden, sowie mit temporären Objekten . Die dynamische Speicherdauer ist mit Objekten verbunden, die durch einen new -Ausdruck erstellt werden, oder mit implizit erzeugten Objekten .
Die Speicherdauerkategorien gelten auch für Referenzen.
Die Speicherdauer von Subobjects und Referenzmitgliedern ist die ihres vollständigen Objekts.
Spezifizierer
Die folgenden Schlüsselwörter sind storage class specifiers :
|
(bis C++11) |
|
(bis C++17) |
- static
|
(seit C++11) |
- extern
- mutable
In einer Dekl-Spezifizierer-Sequenz kann es höchstens einen Speicherklassen-Spezifizierer geben , außer dass thread_local zusammen mit static oder extern (seit C++11) vorkommen kann.
mutable hat keine Auswirkung auf die Speicherdauer. Für die Verwendung siehe const/volatile .
Andere Speicherklassenspezifizierer können in den decl-specifier-seq s der folgenden Deklarationen erscheinen:
| Spezifizierer | Kann in der decl-specifier-seq von erscheinen | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Variablendeklarationen | Funktionsdeklarationen |
Strukturierte Bindungsdeklarationen
(seit C++17) |
|||||||
| Nicht-Mitglied | Mitglied | Nicht-Mitglied | Mitglied | ||||||
| Nicht-Parameter | Funktionsparameter | Nicht-statisch | Statisch | Nicht-statisch | Statisch | ||||
| auto | Nur Blockgültigkeit | Ja | Nein | Nein | Nein | Nein | Nein | N/A | |
| register | Nur Blockgültigkeit | Ja | Nein | Nein | Nein | Nein | Nein | N/A | |
| static | Ja | Nein | Deklariert statisch | Nur Namensraumgültigkeit | Deklariert statisch | Ja | |||
| thread_local | Ja | Nein | Nein | Ja | Nein | Nein | Nein | Ja | |
| extern | Ja | Nein | Nein | Nein | Ja | Nein | Nein | Nein | |
Anonyme Unions können auch mit static deklariert werden.
|
register ist ein Hinweis, dass die so deklarierte Variable intensiv verwendet wird, sodass ihr Wert in einer CPU-Register gespeichert werden kann. Der Hinweis kann ignoriert werden, und in den meisten Implementierungen wird er ignoriert, wenn die Adresse der Variable genommen wird. Diese Verwendung ist veraltet. |
(until C++17) |
Statische Speicherdauer
Eine Variable, die alle folgenden Bedingungen erfüllt, hat static storage duration :
- Es gehört zu einem Namensbereich (namespace scope) oder wird erstmals mit static oder extern deklariert.
|
(since C++11) |
Der Speicher für diese Entitäten besteht für die Dauer des Programms.
Thread-SpeicherdauerAlle Variablen, die mit thread_local deklariert werden, besitzen Thread-Speicherdauer . Die Speicherdauer dieser Entitäten entspricht der Lebensdauer des Threads, in dem sie erzeugt wurden. Es existiert ein separates Objekt oder Referenz pro Thread, und die Verwendung des deklarierten Namens bezieht sich auf die Entität, die dem aktuellen Thread zugeordnet ist. |
(seit C++11) |
Automatische Speicherdauer
Die folgenden Variablen haben automatic storage duration :
- Variablen, die zu einem Blockbereich gehören und nicht explizit als static , thread_local , (since C++11) oder extern deklariert sind. Die Speicherdauer solcher Variablen besteht, bis der Block, in dem sie erzeugt wurden, verlassen wird.
- Variablen, die zu einem Parameterbereich gehören (d.h. Funktionsparameter). Die Speicherdauer eines Funktionsparameters besteht bis unmittelbar nach seiner Zerstörung .
Dynamische Speicherdauer
Objekte, die durch die folgenden Methoden während der Programmausführung erstellt werden, haben dynamic storage duration :
- new Ausdrücke . Der Speicher für solche Objekte wird durch Allokierungsfunktionen bereitgestellt und durch Deallokierungsfunktionen freigegeben.
- Implizite Erzeugung durch andere Mittel. Der Speicher für solche Objekte überlappt mit bestehendem Speicher.
- Ausnahmeobjekte . Der Speicher für solche Objekte wird auf nicht spezifizierte Weise allokiert und deallokiert.
Verknüpfung
Ein Name kann external linkage , module linkage (since C++20) , internal linkage , oder no linkage haben:
- Eine Entität, deren Name externe Verknüpfung hat, kann in einer anderen Übersetzungseinheit neu deklariert werden , und die Neudeklaration kann einem anderen Modul zugeordnet werden (seit C++20) .
|
(seit C++20) |
- Eine Entität, deren Name interne Verknüpfung hat, kann in einem anderen Gültigkeitsbereich in derselben Übersetzungseinheit erneut deklariert werden.
- Eine Entität, deren Name keine Verknüpfung hat, kann nur im selben Gültigkeitsbereich erneut deklariert werden.
Die folgenden Verknüpfungen werden erkannt:
Keine Verknüpfung
Jeder der folgenden Namen, die im Blockbereich deklariert werden, hat keine Bindung:
- Variablen, die nicht explizit deklariert sind extern (unabhängig vom static Modifikator);
- lokale Klassen und ihre Memberfunktionen;
- Andere Namen, die im Blockbereich deklariert sind, wie Typdefinitionen, Enumerationen und Enumeratoren.
Nicht mit externer , Modul-, (seit C++20) oder interner Verknüpfung spezifizierte Namen haben ebenfalls keine Verknüpfung, unabhängig davon, in welchem Geltungsbereich sie deklariert sind.
Interne Verknüpfung
Jeder der folgenden Namen, die im Namespace-Bereich deklariert sind, hat interne Verknüpfung:
- Variablen , Variablentemplates (since C++14) , Funktionen oder Funktionstemplates, die als static deklariert sind;
- Nicht-Template (since C++14) Variablen von nicht-flüchtigem const-qualifiziertem Typ, es sei denn
|
(since C++17) |
|
(since C++20) |
-
- sie sind explizit als extern deklariert, oder
- sie wurden zuvor deklariert und die vorherige Deklaration hatte keine interne Verknüpfung;
- Datenelemente von anonymen Unions .
|
Zusätzlich haben alle Namen, die in unbenannten Namensräumen oder in einem Namensraum innerhalb eines unbenannten Namensraums deklariert sind, selbst solche, die explizit als extern deklariert wurden, interne Verknüpfung. |
(seit C++11) |
Externe Verknüpfung
Variablen und Funktionen mit externer Verknüpfung haben ebenfalls Sprachverknüpfung , was das Verknüpfen von Übersetzungseinheiten ermöglicht, die in verschiedenen Programmiersprachen geschrieben sind.
Jeder der folgenden Namen, die im Namespace-Bereich deklariert sind, besitzt externe Verknüpfung, es sei denn, sie sind in einem unbenannten Namespace deklariert oder ihre Deklarationen sind an ein benanntes Modul angehängt und nicht exportiert (seit C++20) :
- Variablen und Funktionen, die nicht oben aufgeführt sind (d.h. Funktionen, die nicht als static deklariert sind, nicht-konstante Variablen, die nicht als static deklariert sind, und alle Variablen, die als extern deklariert sind);
- Enumerationen;
- Namen von Klassen, deren Memberfunktionen, statischen Datenelementen (const oder nicht), geschachtelten Klassen und Enumerationen, sowie Funktionen, die erstmals durch friend -Deklarationen innerhalb von Klassenkörpern eingeführt werden;
- Namen aller Templates, die nicht oben aufgeführt sind (d.h. keine Funktions-Templates, die als static deklariert sind).
Jeder der folgenden Namen, der zuerst im Blockbereich deklariert wird, hat externe Verknüpfung:
- Namen von Variablen, die als extern deklariert sind;
- Namen von Funktionen.
ModulverknüpfungNamen, die im Namespace-Bereich deklariert werden, haben Modulverknüpfung, wenn ihre Deklarationen an ein benanntes Modul angehängt sind und nicht exportiert werden, und keine interne Verknüpfung haben. |
(since C++20) |
|
Dieser Abschnitt ist unvollständig
Grund: Beschreibung des Verhaltens ergänzen, wenn eine Entität mit unterschiedlichen Verknüpfungen in derselben Übersetzungseinheit deklariert wird (6.6 Absatz 6), den Unterschied zwischen C++20 (nicht konform) und dem aktuellen Entwurf (konform) vermerken |
Statische Blockvariablen
Blockvariablen mit statischer oder Thread- (seit C++11) Speicherdauer werden initialisiert, wenn die Ausführung zum ersten Mal ihre Deklaration durchläuft (sofern ihre Initialisierung nicht Null- oder Konstanteninitialisierung ist, die bereits vor dem ersten Betreten des Blocks durchgeführt werden kann). Bei allen weiteren Aufrufen wird die Deklaration übersprungen.
- Wenn die Initialisierung eine Exception wirft , wird die Variable nicht als initialisiert betrachtet, und die Initialisierung wird beim nächsten Durchlauf der Deklaration erneut versucht.
- Wenn die Initialisierung rekursiv in den Block eintritt, in dem die Variable initialisiert wird, ist das Verhalten undefiniert.
|
(seit C++11) |
Der Destruktor für eine Blockvariable mit statischer Speicherdauer wird beim Programmende aufgerufen , aber nur wenn die Initialisierung erfolgreich stattgefunden hat.
Variablen mit statischer Speicherdauer in allen Definitionen derselben Inline-Funktion (die implizit inline sein kann) verweisen alle auf dasselbe Objekt, das in einer Übersetzungseinheit definiert ist, sofern die Funktion externe Verknüpfung besitzt.
Übersetzungseinheiten-lokale Entitäten
Das Konzept der übersetzungseinheitslokalen Entitäten wurde in C++20 standardisiert, siehe diese Seite für weitere Details.
Eine Entität ist translation-unit-local (oder kurz TU-local ), wenn
- es hat einen Namen mit interner Verknüpfung, oder
- es hat keinen Namen mit Verknüpfung und wird innerhalb der Definition einer TU-lokalen Entität eingeführt, oder
- es ist eine Template oder Template-Spezialisierung, deren Template-Argument oder Template-Deklaration eine TU-lokale Entität verwendet.
Schlechte Dinge (in der Regel Verletzung der ODR ) können passieren, wenn der Typ einer nicht-TU-lokalen Entität von einer TU-lokalen Entität abhängt, oder wenn eine Deklaration , oder ein deduction guide für, (since C++17) einer nicht-TU-lokalen Entität eine TU-lokale Entität außerhalb ihrer
- Funktionskörper für eine nicht-inline Funktion oder Funktionsvorlage
- Initialisierer für eine Variable oder Variablenvorlage
- Friend-Deklarationen in einer Klassendefinition
- Verwendung des Werts einer Variable, wenn die Variable in konstanten Ausdrücken verwendbar ist
|
Solche Verwendungen sind in einer Modulschnittstelleneinheit (außerhalb ihres privaten Modulfragments, falls vorhanden) oder einer Modulpartition nicht zulässig und in jedem anderen Kontext veraltet. Eine Deklaration, die in einer Übersetzungseinheit erscheint, kann keine TU-lokale Entität benennen, die in einer anderen Übersetzungseinheit deklariert ist, die keine Headereinheit ist. Eine für eine Vorlage instanziierte Deklaration erscheint am Punkt der Instanziierung der Spezialisierung. |
(since C++20) |
Hinweise
Namen im obersten Namensraumbereich (Dateibereich in C), die const und nicht extern sind, haben externe Bindung in C, aber interne Bindung in C++.
Seit C++11 ist auto kein Speicherklassenspezifizierer mehr; es wird zur Typableitung verwendet.
|
In C kann die Adresse einer register -Variable nicht genommen werden, aber in C++ ist eine mit register deklarierte Variable semantisch nicht von einer Variable ohne Speicherklassenspezifizierer zu unterscheiden. |
(bis C++17) |
|
In C++ können Variablen im Gegensatz zu C nicht mit register deklariert werden. |
(seit C++17) |
Namen von thread_local Variablen mit interner oder externer Verknüpfung, auf die von verschiedenen Gültigkeitsbereichen aus verwiesen wird, können auf dieselben oder auf verschiedene Instanzen verweisen, abhängig davon, ob der Code im selben oder in verschiedenen Threads ausgeführt wird.
Das extern Schlüsselwort kann auch verwendet werden, um Sprachverknüpfung und explizite Template-Instanziierungsdeklarationen anzugeben, aber es ist in diesen Fällen kein Speicherklassenspezifizierer (außer wenn eine Deklaration direkt in einer Sprachverknüpfungsspezifikation enthalten ist, in welchem Fall die Deklaration so behandelt wird, als ob sie den extern Spezifizierer enthielte).
Speicherklassenspezifizierer, mit Ausnahme von thread_local , sind nicht zulässig bei expliziten Spezialisierungen und expliziten Instanziierungen :
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // "static" erscheint hier nicht
|
Eine const (implizit durch constexpr möglich) Variable Template hatte standardmäßig interne Verknüpfung, was inkonsistent mit anderen templatisierten Entitäten war. Defektbericht CWG2387 korrigierte dies. |
(since C++14) |
inline
dient als Workaround für
CWG2387
durch standardmäßige externe Verknüpfung. Deshalb wurde
inline
zu vielen Variable Templates
hinzugefügt
und nach Annahme von CWG2387 wieder
entfernt
. Standardbibliothek-Implementierungen müssen ebenfalls
inline
verwenden, solange ein unterstützter Compiler CWG2387 nicht implementiert hat. Siehe
GCC Bugzilla #109126
und
MSVC STL PR #4546
.
|
(since C++17) |
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_threadsafe_static_init
|
200806L
|
(C++11) | Dynamische Initialisierung und Destruktion mit Nebenläufigkeit |
Schlüsselwörter
auto , register , static , extern , thread_local , mutable
Beispiel
#include <iostream> #include <mutex> #include <string> #include <thread> thread_local unsigned int rage = 1; std::mutex cout_mutex; void increase_rage(const std::string& thread_name) { ++rage; // modifying outside a lock is okay; this is a thread-local variable std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; } int main() { std::thread a(increase_rage, "a"), b(increase_rage, "b"); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for main: " << rage << '\n'; } a.join(); b.join(); }
Mögliche Ausgabe:
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 216 | C++98 |
unbenannte Klassen und Enumerationen im Klassenbereich haben
andere Verknüpfung als im Namensbereich |
sie haben alle externe
Verknüpfung in diesen Bereichen |
| CWG 389 | C++98 |
ein Name ohne Verknüpfung sollte nicht
verwendet werden, um eine Entität mit Verknüpfung zu deklarieren |
ein Typ ohne Verknüpfung darf nicht verwendet
werden als Typ einer Variable oder Funktion mit Verknüpfung, es sei denn, die Variable oder Funktion hat C-Sprachverknüpfung |
| CWG 426 | C++98 |
eine Entität könnte sowohl mit interner
als auch externer Verknüpfung in derselben Übersetzungseinheit deklariert werden |
das Programm ist in diesem Fall fehlerhaft |
| CWG 527 | C++98 |
die durch die Lösung von CWG 389 eingeführte Typbeschränkung
wurde auch auf Variablen und Funktionen angewendet, die außerhalb ihrer eigenen Übersetzungseinheiten nicht benannt werden können |
die Beschränkung wurde für diese
Variablen und Funktionen aufgehoben (d.h. ohne Verknüpfung oder mit interner Verknüpfung, oder deklariert innerhalb unbenannter Namensräume) |
| CWG 809 | C++98 | register erfüllte kaum eine Funktion | veraltet |
| CWG 1648 | C++11 |
static
war impliziert, selbst wenn
thread_local mit extern kombiniert wird |
nur impliziert, wenn kein anderer Speicher-
klassenspezifizierer vorhanden ist |
| CWG 1686 |
C++98
C++11 |
der Name einer nicht-statischen Variable, die im Namensbereich
deklariert wurde, hatte nur dann interne Verknüpfung, wenn sie explizit als const (C++98) oder constexpr (C++11) deklariert wurde |
erfordert nur, dass der Typ
const-qualifiziert ist |
| CWG 2019 | C++98 |
die Speicherdauer von Referenz-
Mitgliedern war nicht spezifiziert |
gleich wie ihr vollständiges Objekt |
| CWG 2387 | C++14 |
unklar, ob const-qualifizierte Variablen-
templates standardmäßig interne Verknüpfung haben |
const-Qualifizierer beeinflusst nicht
die Verknüpfung von Variablen- templates oder ihren Instanzen |
| CWG 2533 | C++98 |
die Speicherdauer von implizit
erzeugten Objekten war unklar |
klargestellt |
| CWG 2850 | C++98 |
es war unklar, wann der Speicher für
Funktionsparameter freigegeben wird |
klargestellt |
| CWG 2872 | C++98 | die Bedeutung von "kann referenziert werden" war unklar | verbesserte Formulierung |
| P2788R0 | C++20 |
das Deklarieren einer const-qualifizierten Variable in einem Namensbereich
gab ihr interne Verknüpfung, selbst in einer Moduleinheit |
interne Verknüpfung wird nicht vergeben |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 6.7.5 Speicherdauer [basic.stc]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 6.7.5 Speicherdauer [basic.stc]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 6.7 Speicherdauer [basic.stc]
- C++14 Standard (ISO/IEC 14882:2014):
-
- 3.7 Speicherdauer [basic.stc]
- C++11 Standard (ISO/IEC 14882:2011):
-
- 3.7 Speicherdauer [basic.stc]
- C++03 Standard (ISO/IEC 14882:2003):
-
- 3.7 Speicherdauer [basic.stc]
- C++98 Standard (ISO/IEC 14882:1998):
-
- 3.7 Speicherdauer [basic.stc]
Siehe auch
|
C-Dokumentation
für
storage duration
|