Namensräume
Varianten
Aktionen

Speicherklassen-Spezifizierer

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Die Speicherklassen-Spezifizierer sind 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.

Inhalt

[bearbeiten] Speicherdauer

Die Speicherdauer ist die Eigenschaft eines Objekts, die die minimale potenzielle Lebensdauer des den Objekts enthaltenden Speichers bestimmt. Die Speicherdauer wird durch die zur Erzeugung des Objekts verwendete Konstruktion bestimmt und ist eine der folgenden:

  • statische Speicherdauer
  • Thread-Speicherdauer
(seit C++11)
  • automatische Speicherdauer
  • dynamische Speicherdauer

Statische, 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 erzeugt werden, oder mit implizit erzeugten Objekten.

Die Kategorien der Speicherdauer gelten auch für Referenzen.

Die Speicherdauer von Teilobjekten und Referenzmembern ist die ihres vollständigen Objekts.

[bearbeiten] Spezifizierer

Die folgenden Schlüsselwörter sind Speicherklassen-Spezifizierer:

  • auto
(bis C++11)
  • register
(bis C++17)
  • static
  • thread_local
(seit C++11)
  • extern
  • mutable

In einer decl-specifier-seq kann höchstens ein Speicherklassen-Spezifizierer vorhanden sein, außer dass thread_local mit static oder extern auftreten kann(seit C++11).

mutable hat keine Auswirkung auf die Speicherdauer. Siehe const/volatile für die Verwendung.

Andere Speicherklassen-Spezifizierer können in der decl-specifier-seq der folgenden Deklarationen vorkommen:

Spezifizierer Kann in der decl-specifier-seq vorkommen von
Variablendeklarationen Funktionsdeklarationen Strukturelle Bindungsdeklarationen
(seit C++17)
Nicht-Member Mitglied Nicht-Member Mitglied
Nicht-Parameter Funktionsparameter Nicht-statisch  Statisch  Nicht-statisch  Statisch 
auto Nur im Block-Gültigkeitsbereich Ja Nein Nein Nein Nein Nein N/A
register Nur im Block-Gültigkeitsbereich Ja Nein Nein Nein Nein Nein N/A
static Ja Nein Deklariert statisch Nur im Namespace-Gültigkeitsbereich 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 stark genutzt wird, so dass ihr Wert in einem CPU-Register gespeichert werden kann. Der Hinweis kann ignoriert werden, und in den meisten Implementierungen wird er ignoriert, wenn die Adresse der Variablen genommen wird. Diese Verwendung ist veraltet.

(bis C++17)

[bearbeiten] Statische Speicherdauer

Eine Variable, die alle folgenden Bedingungen erfüllt, hat statische Speicherdauer:

  • Sie hat keine Thread-Speicherdauer.
(seit C++11)

Der Speicher für diese Entitäten dauert während der gesamten Programmlaufzeit.

Thread-Speicherdauer

Alle mit thread_local deklarierten Variablen haben Thread-Speicherdauer.

Der Speicher für diese Entitäten dauert während der gesamten Laufzeit des Threads, in dem sie erzeugt werden. Es gibt ein eigenständiges Objekt oder eine eigenständige Referenz pro Thread, und die Verwendung des deklarierten Namens bezieht sich auf die dem aktuellen Thread zugeordnete Entität.

(seit C++11)

[bearbeiten] Automatische Speicherdauer

Die folgenden Variablen haben automatische Speicherdauer:

  • Variablen, die zu einem Block-Gültigkeitsbereich gehören und nicht explizit als static, thread_local,(seit C++11) oder extern deklariert sind. Der Speicher für solche Variablen hält bis zum Ende des Blocks an, in dem sie erzeugt werden.
  • Variablen, die zu einem Parameter-Gültigkeitsbereich gehören (d. h. Funktionsparameter). Der Speicher für einen Funktionsparameter dauert bis unmittelbar nach seiner Zerstörung.

[bearbeiten] Dynamische Speicherdauer

Objekte, die während der Programmausführung durch die folgenden Methoden erzeugt werden, haben dynamische Speicherdauer:

[bearbeiten] Bindung

Ein Name kann externe Bindung, Modulbindung(seit C++20), interne Bindung oder keine Bindung haben.

  • Eine Entität, deren Name Modulbindung hat, kann in einer anderen Übersetzungseinheit neu deklariert werden, solange die Neudeklaration demselben Modul zugeordnet ist.
(seit C++20)
  • Eine Entität, deren Name interne Bindung hat, kann in einem anderen Gültigkeitsbereich in derselben Übersetzungseinheit neu deklariert werden.
  • Eine Entität, deren Name keine Bindung hat, kann nur im selben Gültigkeitsbereich neu deklariert werden.

Die folgenden Bindungen werden erkannt:

[bearbeiten] Keine Bindung

Namen, die im Block-Gültigkeitsbereich deklariert sind, haben keine Bindung:

  • Variablen, die nicht explizit als extern deklariert sind (unabhängig vom Modifikator static);
  • Lokale Klassen und ihre Member-Funktionen;
  • andere Namen, die im Block-Gültigkeitsbereich deklariert sind, wie Typedefs, Aufzählungen und Aufzählungswerte.

Namen, die nicht mit externer, Modul-(seit C++20) oder interner Bindung spezifiziert sind, haben ebenfalls keine Bindung, unabhängig davon, in welchem Gültigkeitsbereich sie deklariert werden.

[bearbeiten] Interne Bindung

Folgende Namen, die im Namespace-Gültigkeitsbereich deklariert sind, haben interne Bindung:

  • Variablen, Variablentemplates(seit C++14), Funktionen oder Funktionstemplates, die als static deklariert sind;
  • Nicht-Template-(seit C++14)Variablen vom Typ nicht-volatile const-qualifiziert, es sei denn,
  • sie sind inline,
(seit C++17)
(seit C++20)
  • sie sind explizit als extern deklariert oder
  • sie wurden zuvor deklariert und die vorherige Deklaration hatte keine interne Bindung;

Darüber hinaus haben alle Namen, die in unbenannten Namespaces oder einem Namespace innerhalb eines unbenannten Namespaces deklariert sind, selbst wenn sie explizit als extern deklariert wurden, interne Bindung.

(seit C++11)

[bearbeiten] Externe Bindung

Variablen und Funktionen mit externer Bindung haben auch eine Sprachbindung, die es ermöglicht, in verschiedenen Programmiersprachen geschriebene Übersetzungseinheiten zu verknüpfen.

Folgende Namen, die im Namespace-Gültigkeitsbereich deklariert sind, haben externe Bindung, es sei denn, sie sind in einem unbenannten Namespace deklariertoder ihre Deklarationen sind an ein benanntes Modul gebunden 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-const Variablen, die nicht als static deklariert sind, und alle Variablen, die als extern deklariert sind);
  • Aufzählungen;
  • Namen von Klassen, ihren Member-Funktionen, statischen Datenmembern (const oder nicht), verschachtelten Klassen und Aufzählungen sowie Funktionen, die zuerst durch friend-Deklarationen innerhalb von Klassenkörpern eingeführt wurden;
  • Namen aller Templates, die nicht oben aufgeführt sind (d. h. keine als static deklarierten Funktionstemplates).

Folgende Namen, die zuerst im Block-Gültigkeitsbereich deklariert sind, haben externe Bindung:

  • Namen von Variablen, die als extern deklariert sind;
  • Namen von Funktionen.

Modulbindung

Namen, die im Namespace-Gültigkeitsbereich deklariert sind, haben Modulbindung, wenn ihre Deklarationen an ein benanntes Modul gebunden und nicht exportiert sind und keine interne Bindung haben.

(seit C++20)

[bearbeiten] Statische Blockvariablen

Blockvariablen mit statischer oder Thread-(seit C++11) Speicherdauer werden beim ersten Durchlauf der Steuerung durch ihre Deklaration initialisiert (es sei denn, ihre Initialisierung ist eine Null- oder Konstantinitialisierung, die vor dem ersten Betreten des Blocks erfolgen kann). Bei allen weiteren Aufrufen wird die Deklaration übersprungen.

  • Wenn die Initialisierung eine Ausnahme auslöst, wird die Variable nicht als initialisiert betrachtet, und die Initialisierung wird beim nächsten Durchlauf der Steuerung durch die Deklaration erneut versucht.
  • Wenn die Initialisierung rekursiv den Block betritt, in dem die Variable gerade initialisiert wird, ist das Verhalten undefiniert.
  • Wenn mehrere Threads versuchen, dieselbe statische lokale Variable gleichzeitig zu initialisieren, erfolgt die Initialisierung genau einmal (ähnliches Verhalten kann für beliebige Funktionen mit std::call_once erzielt werden).
  • Übliche Implementierungen dieser Funktion verwenden Varianten des Double-Checked Locking-Musters, das den Laufzeitaufwand für bereits initialisierte lokale Statics auf einen einzigen nicht-atomaren booleschen Vergleich reduziert.
(seit C++11)

Der Destruktor für eine Blockvariable mit statischer Speicherdauer wird beim Programmende aufgerufen, aber nur, wenn die Initialisierung erfolgreich war.

Variablen mit statischer Speicherdauer in allen Definitionen derselben Inline-Funktion (die implizit inline sein kann) beziehen sich alle auf dasselbe Objekt, das in einer Übersetzungseinheit definiert ist, solange die Funktion externe Bindung hat.

[bearbeiten] Einheitenlokale Entitäten (Translation Unit Local Entities)

Das Konzept der einheitenlokalen Entitäten ist in C++20 standardisiert, siehe diese Seite für weitere Details.

Eine Entität ist einheitenlokal (kurz: TU-lokal), wenn

  • sie einen Namen mit interner Bindung hat, oder
  • sie keinen Namen mit Bindung hat und innerhalb der Definition einer TU-lokalen Entität eingeführt wird, oder
  • sie ein Template oder eine Template-Spezialisierung ist, deren Template-Argument oder Template-Deklaration eine TU-lokale Entität verwendet.

Schlechte Dinge (normalerweise eine Verletzung von 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 vonoder eine Deduktionshilfe für(seit C++17) einer nicht-TU-lokalen Entität eine TU-lokale Entität außerhalb ihres

  • Funktionskörpers für eine nicht-inline Funktion oder ein Funktionstemplate
  • Initialisierungsausdruck für eine Variable oder ein Variablentemplate
  • Friend-Deklarationen in einer Klassendefinition
  • Verwendung des Werts einer Variablen, wenn die Variable für Konstante Ausdrücke 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 auftritt, kann keine TU-lokale Entität benennen, die in einer anderen Übersetzungseinheit deklariert wurde, die keine Header-Einheit ist. Eine Deklaration, die für ein Template instanziiert wird, erscheint an der Stelle der Instanziierung der Spezialisierung.

(seit C++20)

[bearbeiten] Anmerkungen

Namen im obersten Namespace-Gültigkeitsbereich (Datei-Gültigkeitsbereich in C), die const sind und nicht extern sind, haben in C externe Bindung, aber in C++ interne Bindung.

Seit C++11 ist auto kein Speicherklassen-Spezifizierer mehr; es wird zur Angabe der Typableitung verwendet.

In C kann die Adresse einer register-Variable nicht genommen werden, aber in C++ ist eine als register deklarierte Variable semantisch nicht von einer ohne Speicherklassen-Spezifizierer deklarierten Variable zu unterscheiden.

(bis C++17)

In C++ können im Gegensatz zu C Variablen nicht als register deklariert werden.

(seit C++17)

Namen von thread_local-Variablen mit interner oder externer Bindung, auf die von verschiedenen Gültigkeitsbereichen zugegriffen wird, können sich auf dieselben oder auf unterschiedliche Instanzen beziehen, je nachdem, ob der Code im selben oder in verschiedenen Threads ausgeführt wird.

Das Schlüsselwort extern kann auch zur Angabe der Sprachbindung und zur expliziten Template-Instanziierung verwendet werden, ist aber in diesen Fällen kein Speicherklassen-Spezifizierer (außer wenn eine Deklaration direkt in einer Spezifikation der Sprachbindung enthalten ist, in welchem Fall die Deklaration so behandelt wird, als enthielte sie den extern-Spezifizierer).

Speicherklassen-Spezifizierer, mit Ausnahme von thread_local, sind auf expliziten Spezialisierungen und expliziten Instanziierungen nicht zulässig.

template<class T>
struct S
{
    thread_local static int tlm;
};
 
template<>
thread_local int S<float>::tlm = 0; // "static" does not appear here

Ein const (kann durch constexpr impliziert sein) Variablentemplate hatte standardmäßig eine interne Bindung, was inkonsistent mit anderen Templat-Entitäten war. Der Fehlerbericht CWG2387 hat dies korrigiert.

(seit C++14)
inline dient als Workaround für CWG2387, indem es standardmäßig externe Bindung gewährt. Deshalb wurde inline zu vielen Variablentemplates hinzugefügt und dann nach der Annahme von CWG2387 entfernt. Standardbibliotheksimplementierungen müssen ebenfalls inline verwenden, solange ein unterstützter Compiler CWG2387 noch nicht implementiert hat. Siehe GCC Bugzilla #109126 und MSVC STL PR #4546. (seit C++17)
Feature-Testmakro Wert Std Feature
__cpp_threadsafe_static_init 200806L (C++11) Dynamische Initialisierung und Zerstörung mit Nebenläufigkeit

[bearbeiten] Schlüsselwörter

auto, register, static, extern, thread_local, mutable

[bearbeiten] 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

[bearbeiten] 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 Klasse und Aufzählung im Klassengültigkeitsbereich haben
unterschiedliche Bindung als im Namespace-Gültigkeitsbereich
sie alle haben externe
Bindung in diesen Gültigkeitsbereichen
CWG 389 C++98 ein Name ohne Bindung sollte nicht
verwendet werden, um eine Entität mit Bindung zu deklarieren
ein Typ ohne Bindung darf nicht verwendet werden
als Typ einer Variablen oder Funktion
mit Bindung, es sei denn, die Variable
oder Funktion hat C-Sprachbindung
CWG 426 C++98 eine Entität könnte mit interner
und externer Bindung in derselben Übersetzungseinheit deklariert werden
Das Programm ist in diesem Fall schlecht geformt.
CWG 527 C++98 die Typbeschränkung, die durch die Auflösung von CWG
389 eingeführt wurde, wurde auch auf Variablen und Funktionen angewendet, die
nicht außerhalb ihrer eigenen Übersetzungseinheiten benannt werden können
die Beschränkung wird für diese aufgehoben
Variablen und Funktionen (d. h. ohne
Bindung oder interne Bindung, oder deklariert
innerhalb unbenannter Namespaces)
CWG 809 C++98 register hatte sehr wenig Funktion deprecated
CWG 1648 C++11 static wurde impliziert, auch wenn
thread_local mit extern kombiniert wird
impliziert nur, wenn kein anderer Speicher
klassen-Spezifizierer vorhanden ist
CWG 1686 C++98
C++11
der Name einer nicht-statischen Variablen, deklariert im Namespace
Gültigkeitsbereich, hatte interne Bindung nur, wenn sie explizit
als const (C++98) oder constexpr (C++11) deklariert wurde
nur der Typ erforderlich war
const-qualifiziert zu sein
CWG 2019 C++98 die Speicherdauer von Referenz
Membern war unbestimmt
dieselbe wie ihr vollständiges Objekt
CWG 2387 C++14 unklar, ob const-qualifizierte Variablen-
Templates standardmäßig interne Bindung haben
const-Qualifizierer beeinflusst nicht
die Bindung von Variablen
Templates oder ihre Instanzen
CWG 2533 C++98 die Speicherdauer von implizit-
erzeugten Objekten war unklar
wurde klargestellt
CWG 2850 C++98 war unklar, wann der Speicher für
Funktionsparameter freigegeben wird
wurde klargestellt
CWG 2872 C++98 die Bedeutung von „kann referenziert werden“ war unklar verbesserte Formulierung
P2788R0 C++20 die Deklaration einer const-qualifizierten Variable in einem Namespace
gab ihr interne Bindung, selbst in einer Modul-Einheit
interne Bindung wird nicht vergeben

[bearbeiten] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 6.7.5 Storage duration [basic.stc]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 6.7.5 Storage duration [basic.stc]
  • C++17 Standard (ISO/IEC 14882:2017)
  • 6.7 Storage duration [basic.stc]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 3.7 Storage duration [basic.stc]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 3.7 Storage duration [basic.stc]
  • C++03-Standard (ISO/IEC 14882:2003)
  • 3.7 Storage duration [basic.stc]
  • C++98 Standard (ISO/IEC 14882:1998)
  • 3.7 Storage duration [basic.stc]

[bearbeiten] Siehe auch

C-Dokumentation für Speicherdauer