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 extern
specyfikatorów , static
lub 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++, aregister
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.
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ść.Podczas deklarowania zmiennej w funkcji słowo kluczowe określa,
static
że zmienna zachowuje swój stan między wywołaniami tej funkcji.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. Elementstatic
członkowski danych musi być zdefiniowany w zakresie pliku. Integralny element członkowski danych zadeklarowany jakoconst static
może mieć inicjator.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. Funkcjastatic
składowa nie może uzyskać dostępu do elementu członkowskiego wystąpienia, ponieważ funkcja nie ma niejawnegothis
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.Nie można zadeklarować członków obiektu
union
jakostatic
. Jednak globalnie zadeklarowany anonimowyunion
musi być jawnie zadeklarowanystatic
.
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 extern
Jednostki 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 elementemstatic
lubextern
.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 (istatic
extern
), lokalne obiekty statyczne i statyczne składowe danych klas. Każda zadeklarowanathread_local
zmienna lokalna jest niejawnie statyczna, jeśli nie podano żadnej innej klasy magazynu; innymi słowy, w zakresiethread_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 zstd::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
, I2
i 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
, I2
i 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
iI3
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ż
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla