Speicherklassen

Eine Speicherklasse im Kontext von C++-Variablendeklarationen ist ein Typbezeichner, der die Lebensdauer, Verknüpfung und Speicherort von Objekten steuert. Ein angegebenes Objekt kann nur eine Speicherklasse haben. Variablen, die in einem Block definiert sind, weisen automatischen Speicher auf, sofern dies nicht mit den externstaticBezeichnern oder thread_local Bezeichnern anders angegeben wird. Automatische Objekte und Variablen haben keine Verknüpfung; sie sind für Code außerhalb des Blocks nicht sichtbar. Der Speicher wird automatisch zugewiesen, wenn die Ausführung in den Block wechselt und beim Beenden des Blocks aufgehoben wird.

Hinweise

  • Die mutable Schlüsselwort (keyword) kann als Speicherklassenbezeichner betrachtet werden. Sie ist jedoch nur in der Memberliste einer Klassendefinition verfügbar.

  • Visual Studio 2010 und höher: Die auto Schlüsselwort (keyword) ist kein C++-Speicherklassenbezeichner mehr, und die register Schlüsselwort (keyword) ist veraltet. Visual Studio 2017, Version 15.7 und höher: (im /std:c++17 Modus und höher verfügbar): Die register Schlüsselwort (keyword) wird aus der C++-Sprache entfernt. Die Verwendung bewirkt eine Diagnosenachricht:

    // c5033.cpp
    // compile by using: cl /c /std:c++17 c5033.cpp
    register int value; // warning C5033: 'register' is no longer a supported storage class
    

static

Die static Schlüsselwort (keyword) können verwendet werden, um Variablen und Funktionen im globalen Bereich, Namespacebereich und Klassenbereich zu deklarieren. Statische Variablen können auch im lokalen Gültigkeitsbereich deklariert werden.

Statische Dauer bedeutet, dass das Objekt oder die Variable zugewiesen wird, wenn das Programm gestartet wird. Die Zuweisung wird wieder aufgehoben, wenn das Programm beendet wird. Externe Verknüpfung bedeutet, dass der Name der Variablen außerhalb der Datei sichtbar ist, in der die Variable deklariert wird. Im Gegensatz dazu bedeutet die interne Verknüpfung, dass der Name außerhalb der Datei, in der die Variable deklariert wird, nicht sichtbar ist. Standardmäßig verfügt ein Objekt oder eine Variable, das bzw. die im globalen Namespace definiert wird, über eine statische Dauer und externe Verknüpfung. Die static Schlüsselwort (keyword) können in den folgenden Situationen verwendet werden.

  1. Wenn Sie eine Variable oder Funktion im Dateibereich (globaler und/oder Namespacebereich) deklarieren, gibt die static Schlüsselwort (keyword) an, dass die Variable oder Funktion über eine interne Verknüpfung verfügt. Wenn Sie eine Variable deklarieren, hat die Variable eine statische Dauer und wird vom Compiler mit dem Wert 0 initialisiert, solange Sie keinen anderen Wert angeben.

  2. Wenn Sie eine Variable in einer Funktion deklarieren, gibt die static Schlüsselwort (keyword) an, dass die Variable ihren Zustand zwischen Aufrufen dieser Funktion behält.

  3. Wenn Sie ein Datenmembe in einer Klassendeklaration deklarieren, gibt die static Schlüsselwort (keyword) an, dass eine Kopie des Elements von allen Instanzen der Klasse gemeinsam verwendet wird. Ein static Datenmememm muss im Dateibereich definiert werden. Ein integrales Datenelement, das Sie als Initialisierer deklarieren const static .

  4. Wenn Sie eine Memberfunktion in einer Klassendeklaration deklarieren, gibt die static Schlüsselwort (keyword) an, dass die Funktion von allen Instanzen der Klasse gemeinsam verwendet wird. Eine static Memberfunktion kann nicht auf ein Instanzelement zugreifen, da die Funktion keinen impliziten this Zeiger hat. Um auf ein Instanzmememm zuzugreifen, deklarieren Sie die Funktion mit einem Parameter, der einen Instanzzeiger oder -verweis darstellt.

  5. Sie können die Member einer union Als staticnicht deklarieren. Allerdings muss eine global deklarierte anonyme union Person explizit deklariert staticwerden.

In diesem Beispiel wird gezeigt, wie eine in einer Funktion deklarierte static Variable ihren Zustand zwischen Aufrufen dieser Funktion behält.

// static1.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
void showstat( int curr ) {
   static int nStatic;    // Value of nStatic is retained
                          // between each function call
   nStatic += curr;
   cout << "nStatic is " << nStatic << endl;
}

int main() {
   for ( int i = 0; i < 5; i++ )
      showstat( i );
}
nStatic is 0
nStatic is 1
nStatic is 3
nStatic is 6
nStatic is 10

Dieses Beispiel zeigt die Verwendung static in einer Klasse.

// static2.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
class CMyClass {
public:
   static int m_i;
};

int CMyClass::m_i = 0;
CMyClass myObject1;
CMyClass myObject2;

int main() {
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject1.m_i = 1;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject2.m_i = 2;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   CMyClass::m_i = 3;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;
}
0
0
1
1
2
2
3
3

Das folgende Beispiel zeigt eine lokale Variable, die in einer Memberfunktion deklariert ist static . Die static Variable ist für das gesamte Programm verfügbar. Alle Instanzen des Typs verwenden dieselbe Kopie der static Variablen.

// static3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct C {
   void Test(int value) {
      static int var = 0;
      if (var == value)
         cout << "var == value" << endl;
      else
         cout << "var != value" << endl;

      var = value;
   }
};

int main() {
   C c1;
   C c2;
   c1.Test(100);
   c2.Test(100);
}
var != value
var == value

Ab C++11 static ist eine lokale Variableninitialisierung garantiert threadsicher. Dieses Feature wird manchmal als magische Statik bezeichnet. Allerdings müssen alle nachfolgende Zuweisungen in einer Multithreadanwendung synchronisiert werden. Die threadsichere statische Initialisierungsfunktion kann mithilfe des /Zc:threadSafeInit- Flags deaktiviert werden, um zu vermeiden, dass eine Abhängigkeit vom CRT verwendet wird.

extern

Objekte und Variablen, die als extern Deklarieren eines Objekts deklariert werden, das in einer anderen Übersetzungseinheit oder in einem eingeschlossenen Bereich als externe Verknüpfung definiert ist. Weitere Informationen finden Sie unter extern Übersetzungseinheiten und Verknüpfungen.

thread_local (C++11)

Auf eine variable, die mit dem thread_local Bezeichner deklariert wird, kann nur im Thread zugegriffen werden, auf den sie erstellt wird. Die Variable wird erstellt, wenn der Thread erstellt wird, und er wird zerstört, wenn der Thread zerstört wird. Jeder Thread verfügt über eine eigene Kopie der Variable. Unter Windows thread_local entspricht die Funktion dem microsoftspezifischen __declspec( thread ) Attribut.

thread_local float f = 42.0; // Global namespace. Not implicitly static.

struct S // cannot be applied to type definition
{
    thread_local int i; // Illegal. The member must be static.
    thread_local static char buf[10]; // OK
};

void DoSomething()
{
    // Apply thread_local to a local variable.
    // Implicitly "thread_local static S my_struct".
    thread_local S my_struct;
}

Hinweise zum thread_local Bezeichner:

  • Dynamisch initialisierte threadlokale Variablen in DLLs werden in allen aufrufenden Threads möglicherweise nicht ordnungsgemäß initialisiert. Weitere Informationen finden Sie unter thread.

  • Der thread_local Bezeichner kann mit static oder extern.

  • Sie können nur auf Datendeklarationen und Definitionen angewendet thread_local werden. thread_local Sie können nicht für Funktionsdeklarationen oder Definitionen verwendet werden.

  • Sie können nur für Datenelemente mit statischer Speicherdauer angebenthread_local, die globale Datenobjekte (sowohl als externauch static ), lokale statische Objekte und statische Datenelemente von Klassen umfasst. Jede deklarierte thread_local lokale Variable ist implizit statisch, wenn keine andere Speicherklasse bereitgestellt wird, d. h. im Blockbereich thread_local entspricht thread_local static.

  • Sie müssen das thread_local für die Deklaration und Definition eines lokalen Threadobjekts angeben, egal ob die Deklaration und Definition in der gleichen Datei oder in separaten Dateien auftreten.

  • Es wird nicht empfohlen, thread_local Variablen mit std::launch::async. Weitere Informationen finden Sie unter <future> Funktionen.

Unter Windows ist funktional gleichbedeutend mit __declspec(thread) der Ausnahme, thread_local dass *__declspec(thread)* auf eine Typdefinition angewendet werden kann und in C-Code gültig ist. Verwenden Sie thread_local nach Möglichkeit, da sie Teil des C++-Standards ist und daher portierbarer ist.

Registrieren

Visual Studio 2017, Version 15.3 und höher (im /std:c++17 Modus und höher verfügbar): Die register Schlüsselwort (keyword) ist keine unterstützte Speicherklasse mehr. Die Verwendung bewirkt eine Diagnose. Die Schlüsselwort (keyword) ist weiterhin im Standard für die zukünftige Verwendung reserviert.

   register int val; // warning C5033: 'register' is no longer a supported storage class

Beispiel: automatische und statische Initialisierung

Ein lokales automatisches Objekt oder eine lokale automatische Variable wird jedes Mal initialisiert, wenn die Ablaufsteuerung ihre Definition erreicht. Ein lokales automatisches Objekt oder eine lokale automatische Variable wird erstmalig initialisiert, wenn die Ablaufsteuerung ihre Definition erreicht.

Betrachten Sie das folgende Beispiel, in dem eine Klasse definiert wird, die die Initialisierung und Beschädigung von Objekten protokolliert und dann drei Objekte, I1, I2 und I3, definiert:

// initialization_of_objects.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
using namespace std;

// Define a class that logs initializations and destructions.
class InitDemo {
public:
    InitDemo( const char *szWhat );
    ~InitDemo();

private:
    char *szObjName;
    size_t sizeofObjName;
};

// Constructor for class InitDemo
InitDemo::InitDemo( const char *szWhat ) :
    szObjName(NULL), sizeofObjName(0) {
    if ( szWhat != 0 && strlen( szWhat ) > 0 ) {
        // Allocate storage for szObjName, then copy
        // initializer szWhat into szObjName, using
        // secured CRT functions.
        sizeofObjName = strlen( szWhat ) + 1;

        szObjName = new char[ sizeofObjName ];
        strcpy_s( szObjName, sizeofObjName, szWhat );

        cout << "Initializing: " << szObjName << "\n";
    }
    else {
        szObjName = 0;
    }
}

// Destructor for InitDemo
InitDemo::~InitDemo() {
    if( szObjName != 0 ) {
        cout << "Destroying: " << szObjName << "\n";
        delete szObjName;
    }
}

// Enter main function
int main() {
    InitDemo I1( "Auto I1" ); {
        cout << "In block.\n";
        InitDemo I2( "Auto I2" );
        static InitDemo I3( "Static I3" );
    }
    cout << "Exited block.\n";
}
Initializing: Auto I1
In block.
Initializing: Auto I2
Initializing: Static I3
Destroying: Auto I2
Exited block.
Destroying: Auto I1
Destroying: Static I3

In diesem Beispiel wird veranschaulicht, wie und wann die Objekte I1I2, und wann sie zerstört werden.I3

Es gibt mehrere Punkte, die sie zu dem Programm beachten müssen:

  • Zuerst und I2 werden automatisch zerstört, I1 wenn der Kontrollfluss den Block verlässt, in dem sie definiert sind.

  • Zweitens ist es in C++ nicht erforderlich, Objekte oder Variablen am Anfang eines Blocks zu deklarieren. Außerdem werden diese Objekte nur initialisiert, wenn die Ablaufsteuerung deren Definitionen erreicht. (I2 beispiele I3 für solche Definitionen.) Die Ausgabe zeigt genau an, wann sie initialisiert werden.

  • Schließlich werden statische lokale Variablen, z I3 . B. ihre Werte beibehalten, während das Programm ausgeführt wird, aber zerstört, wenn das Programm beendet wird.

Siehe auch

Deklarationen und Definitionen