Klasy magazynu

Klasa magazynu w kontekście deklaracji zmiennych języka C++ jest specyfikatorem typu, który zarządza okresem istnienia, łączeniem i lokalizacją pamięci obiektów. Dany obiekt może mieć tylko jedną klasę magazynu. Zmienne zdefiniowane w bloku mają automatyczny magazyn, chyba że określono inaczej przy użyciu externspecyfikatorów , staticlub thread_local . Automatyczne obiekty i zmienne nie mają powiązania; nie są one widoczne dla kodu poza blokiem. Pamięć jest przydzielana automatycznie po wejściu do bloku i zostanie ona coniętą po zakończeniu bloku.

Uwagi

  • Słowo mutable kluczowe może być traktowane jako specyfikator klasy magazynu. Jednak jest ona dostępna tylko na liście składowej definicji klasy.

  • Visual Studio 2010 i nowsze:auto słowo kluczowe nie jest już specyfikatorem klasy magazynu C++, a register słowo kluczowe jest przestarzałe. Program Visual Studio 2017 w wersji 15.7 lub nowszej: (dostępny w /std:c++17 trybie i nowszym): register słowo kluczowe jest usuwane z języka C++. Jego użycie powoduje komunikat diagnostyczny:

    // 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

Słowo static kluczowe może służyć do deklarowania zmiennych i funkcji w zakresie globalnym, zakresie przestrzeni nazw i zakresie klasy. Zmienne statyczne można również zadeklarować w zakresie lokalnym.

Statyczny czas trwania oznacza, że obiekt lub zmienna jest przydzielana po uruchomieniu programu i zostanie cofnięto przydział po zakończeniu programu. Połączenie zewnętrzne oznacza, że nazwa zmiennej jest widoczna spoza pliku, w którym zadeklarowana jest zmienna. Z drugiej strony połączenie wewnętrzne oznacza, że nazwa nie jest widoczna poza plikiem, w którym zadeklarowana jest zmienna. Domyślnie obiekt lub zmienna zdefiniowana w globalnej przestrzeni nazw ma statyczny czas trwania i połączenie zewnętrzne. Słowo static kluczowe może być używane w następujących sytuacjach.

  1. Podczas deklarowania zmiennej lub funkcji w zakresie pliku (globalny i/lub zakres przestrzeni nazw) static słowo kluczowe określa, że zmienna lub funkcja ma wewnętrzne powiązania. Podczas deklarowania zmiennej zmienna ma statyczny czas trwania i kompilator inicjuje ją do wartości 0, chyba że określisz inną wartość.

  2. Podczas deklarowania zmiennej w funkcji słowo kluczowe określa, static że zmienna zachowuje swój stan między wywołaniami tej funkcji.

  3. Podczas deklarowania składowej danych w deklaracji klasy słowo kluczowe określa, static że jedna kopia elementu członkowskiego jest współużytkowany przez wszystkie wystąpienia klasy. Element static członkowski danych musi być zdefiniowany w zakresie pliku. Integralny element członkowski danych zadeklarowany jako const static może mieć inicjator.

  4. Podczas deklarowania funkcji składowej w deklaracji klasy słowo kluczowe określa, static że funkcja jest współużytkowany przez wszystkie wystąpienia klasy. Funkcja static składowa nie może uzyskać dostępu do elementu członkowskiego wystąpienia, ponieważ funkcja nie ma niejawnego this wskaźnika. Aby uzyskać dostęp do elementu członkowskiego wystąpienia, zadeklaruj funkcję za pomocą parametru, który jest wskaźnikiem wystąpienia lub odwołaniem.

  5. Nie można zadeklarować członków obiektu union jako static. Jednak globalnie zadeklarowany anonimowy union musi być jawnie zadeklarowany static.

W tym przykładzie pokazano, jak zmienna zadeklarowana static w funkcji zachowuje swój stan między wywołaniami tej funkcji.

// 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

W tym przykładzie pokazano użycie klasy static .

// 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

W poniższym przykładzie pokazano zmienną lokalną zadeklarowaną static w funkcji składowej. Zmienna static jest dostępna dla całego programu; wszystkie wystąpienia typu współdzielą tę samą kopię zmiennej static .

// 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

Począwszy od języka C++11, static inicjowanie zmiennej lokalnej gwarantuje bezpieczeństwo wątków. Ta funkcja jest czasami nazywana magicznymi statycznymi. Jednak w aplikacji wielowątku wszystkie kolejne przypisania muszą być zsynchronizowane. Funkcję inicjowania statycznego bezpiecznego wątkowo można wyłączyć przy użyciu /Zc:threadSafeInit- flagi , aby uniknąć zależności od CRT.

extern

Obiekty i zmienne zadeklarowane jako extern deklarują obiekt zdefiniowany w innej jednostce tłumaczenia lub w otaczającym zakresie jako mający połączenie zewnętrzne. Aby uzyskać więcej informacji, zobacz externJednostki tłumaczenia i powiązania.

thread_local (C++11)

Zmienna zadeklarowana za thread_local pomocą specyfikatora jest dostępna tylko w wątku, w którym został utworzony. Zmienna jest tworzona po utworzeniu wątku i jest niszczona podczas niszczenia wątku. Każdy wątek ma własną kopię zmiennej. W systemie Windows thread_local jest funkcjonalnie równoważny atrybutowi specyficznemu dla __declspec( thread ) firmy Microsoft.

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;
}

Kwestie do zanotowania specyfikatora thread_local :

  • Dynamiczne inicjowanie zmiennych lokalnych wątków w bibliotekach DLL może nie być poprawnie zainicjowane we wszystkich wątkach wywołujących. W celu uzyskania więcej informacji, zobacz następujący temat: thread.

  • Specyfikator thread_local może być połączony z elementem static lub extern.

  • Można stosować thread_local tylko do deklaracji i definicji danych. thread_local Nie można ich używać w deklaracjach funkcji ani definicjach.

  • Można określić thread_local tylko elementy danych ze statycznym czasem trwania magazynu, który obejmuje globalne obiekty danych (i staticextern), lokalne obiekty statyczne i statyczne składowe danych klas. Każda zadeklarowana thread_local zmienna lokalna jest niejawnie statyczna, jeśli nie podano żadnej innej klasy magazynu; innymi słowy, w zakresie thread_local bloku jest równoważne .thread_local static

  • Należy określić thread_local zarówno dla deklaracji, jak i definicji obiektu lokalnego wątku, niezależnie od tego, czy deklaracja i definicja występują w tym samym pliku, czy w oddzielnych plikach.

  • Nie zalecamy używania thread_local zmiennych z std::launch::async. Aby uzyskać więcej informacji, zobacz <future> funkcje.

W systemie Windows jest funkcjonalnie równoważny __declspec(thread) z tą różnicą, thread_local że *__declspec(thread)* można zastosować do definicji typu i jest prawidłowy w kodzie C. Jeśli to możliwe, należy użyć thread_local , ponieważ jest częścią standardu C++ i dlatego jest bardziej przenośna.

zarejestruj

Program Visual Studio 2017 w wersji 15.3 lub nowszej (dostępny w /std:c++17 trybie i nowszym): słowo register kluczowe nie jest już obsługiwaną klasą magazynu. Jego użycie powoduje diagnostykę. Słowo kluczowe jest nadal zarezerwowane w standardzie do użycia w przyszłości.

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

Przykład: automatyczne a statyczne inicjowanie

Lokalny obiekt automatyczny lub zmienna jest inicjowany za każdym razem, gdy przepływ kontrolki osiągnie jego definicję. Lokalny obiekt statyczny lub zmienna jest inicjowany po raz pierwszy, gdy przepływ kontrolki osiągnie jego definicję.

Rozważmy poniższy przykład, który definiuje klasę, która rejestruje inicjowanie i niszczenie obiektów, a następnie definiuje trzy obiekty, I1, I2i I3:

// 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

W tym przykładzie pokazano, jak i kiedy obiekty I1, I2i I3 są inicjowane i kiedy są niszczone.

Istnieje kilka punktów, które należy zwrócić uwagę na program:

  • Najpierw i I2 są automatycznie niszczone, I1 gdy przepływ sterowania zamyka blok, w którym są zdefiniowane.

  • Po drugie, w języku C++, nie jest konieczne deklarowanie obiektów ani zmiennych na początku bloku. Ponadto te obiekty są inicjowane tylko wtedy, gdy przepływ kontrolki osiągnie ich definicje. (I2 i I3 są przykładami takich definicji). Dane wyjściowe pokazują dokładnie, kiedy są inicjowane.

  • Na koniec statyczne zmienne lokalne, takie jak I3 zachowywanie ich wartości podczas uruchamiania programu, ale są niszczone, gdy program kończy działanie.

Zobacz też

Deklaracje i definicje