Třídy úložiště
Třída úložiště v kontextu deklarací proměnných jazyka C++ je specifikátor typu, který řídí životnost, propojení a umístění paměti objektů. Předaný objekt může mít pouze jednu třídu úložiště. Proměnné definované v rámci bloku mají automatické úložiště, pokud neurčíte jinak pomocí extern
specifikátorů , static
nebo thread_local
specifikátorů. Automatické objekty a proměnné nemají žádné propojení; nejsou viditelné pro kód mimo blok. Paměť se pro ně při zadávání bloku automaticky přidělí a při ukončení bloku se zruší přidělení.
Poznámky
Klíčové
mutable
slovo může být považováno za specifikátor třídy úložiště. Je však k dispozici pouze v seznamu členů definice třídy.Visual Studio 2010 a novější: Klíčové
auto
slovo už není specifikátorem třídy úložiště C++ aregister
klíčové slovo je zastaralé. Visual Studio 2017 verze 15.7 a novější: (dostupné v/std:c++17
režimu a novější): Klíčovéregister
slovo se odebere z jazyka C++. Jeho použití způsobí diagnostickou zprávu:// 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
Klíčové static
slovo lze použít k deklaraci proměnných a funkcí v globálním oboru, oboru názvů a oboru třídy. Statické proměnné lze deklarovat také v místním oboru.
Statická doba trvání znamená, že objekt nebo proměnná se při spuštění programu přidělí a uvolní se při ukončení programu. Externí propojení znamená, že název proměnné je viditelný mimo soubor, ve kterém je proměnná deklarována. Naopak interní propojení znamená, že název není viditelný mimo soubor, kde je proměnná deklarována. Ve výchozím nastavení má objekt nebo proměnnou definovanou v globálním oboru názvů statickou dobu trvání a externí propojení. Klíčové static
slovo lze použít v následujících situacích.
Když deklarujete proměnnou nebo funkci v oboru souborů (globální obor oboru názvů nebo oboru názvů),
static
klíčové slovo určuje, že proměnná nebo funkce má interní propojení. Když deklarujete proměnnou, má proměnná statickou dobu trvání a kompilátor ji inicializuje na hodnotu 0, pokud nezadáte jinou hodnotu.Když deklarujete proměnnou ve funkci, klíčové slovo určuje,
static
že proměnná uchovává svůj stav mezi voláními této funkce.Když deklarujete datový člen v deklaraci třídy,
static
klíčové slovo určuje, že jedna kopie člena je sdílena všemi instancemi třídy. Datovýstatic
člen musí být definován v oboru souboru. Integrální datový člen, který deklarujete jakoconst static
může mít inicializátor.Když deklarujete členskou funkci v deklaraci třídy,
static
klíčové slovo určuje, že funkce je sdílena všemi instancemi třídy. Členovástatic
funkce nemůže získat přístup k členu instance, protože funkce nemá implicitníthis
ukazatel. Pokud chcete získat přístup k členu instance, deklarujte funkci pomocí parametru, který je ukazatelem instance nebo odkazem.Nemůžete deklarovat členy
union
jakostatic
. Globálně deklarovaný anonymníunion
však musí být explicitně deklarovánstatic
.
Tento příklad ukazuje, jak proměnná deklarovaná static
ve funkci zachovává svůj stav mezi voláními této funkce.
// 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
Tento příklad ukazuje použití static
ve třídě.
// 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
Následující příklad ukazuje místní proměnnou deklarovanou static
v členské funkci. Proměnná static
je k dispozici pro celý program; všechny instance typu sdílejí stejnou kopii static
proměnné.
// 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
Od C++11 je zaručeno, static
že inicializace místních proměnných je bezpečná pro přístup z více vláken. Tato funkce se někdy označuje jako magická statická. Ve vícevláknové aplikaci je však nutné synchronizovat všechna následná přiřazení. Pomocí příznaku /Zc:threadSafeInit-
lze zakázat funkci statické inicializace bezpečné pro přístup z více vláken, aby se zabránilo závislosti na CRT.
extern
Objekty a proměnné deklarované jako extern
deklarují objekt, který je definován v jiné jednotce překladu nebo v uzavřeném oboru, jako má externí propojení. Další informace najdete v článcích extern
a jednotkách překladu a propojení.
thread_local
(C++11)
Proměnná deklarovaná pomocí specifikátoru thread_local
je přístupná pouze ve vlákně, na kterém je vytvořena. Proměnná se vytvoří při vytvoření vlákna a je zničena při zničení vlákna. Každé vlákno má vlastní kopii proměnné. Ve Windows thread_local
je funkčně ekvivalentní atributu specifickému pro __declspec( thread )
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;
}
Co je potřeba vědět o specifikátoru thread_local
:
Dynamicky inicializované proměnné thread-local v knihovnách DLL nemusí být správně inicializovány ve všech volajících vláknech. Další informace najdete na webu
thread
.thread_local
Specifikátor může být kombinován sstatic
neboextern
.Můžete použít
thread_local
pouze u deklarací a definic dat.thread_local
Nejde použít u deklarací funkcí nebo definic.Můžete zadat
thread_local
pouze datové položky se statickou dobou trvání úložiště, která zahrnuje globální datové objekty (astatic
extern
), místní statické objekty a statické datové členy tříd. Jakákoli místní proměnná deklarovanáthread_local
implicitně statická, pokud není k dispozici žádná jiná třída úložiště; jinými slovy, v rozsahuthread_local
bloku je ekvivalentníthread_local static
.Je nutné zadat
thread_local
pro deklaraci i definici místního objektu vlákna, zda deklarace a definice probíhají ve stejném souboru nebo v samostatných souborech.Nedoporučujeme používat
thread_local
proměnné sstd::launch::async
. Další informace najdete v tématu<future>
funkce.
Ve Windows je funkčně ekvivalentní s tím rozdílem__declspec(thread)
, thread_local
že *__declspec(thread)
* lze použít na definici typu a je platný v kódu jazyka C. Kdykoli je to možné, použijte thread_local
ji, protože je součástí standardu C++, a proto je přenosnější.
register
Visual Studio 2017 verze 15.3 a novější (dostupné v /std:c++17
režimu a novější): Klíčové register
slovo už není podporovanou třídou úložiště. Jeho použití způsobuje diagnostiku. Klíčové slovo je stále rezervované ve standardu pro budoucí použití.
register int val; // warning C5033: 'register' is no longer a supported storage class
Příklad: automatická vs. statická inicializace
Místní automatický objekt nebo proměnná se inicializuje pokaždé, když tok řízení dosáhne své definice. Místní statický objekt nebo proměnná se inicializuje při prvním dosažení definice toku řízení.
Představte si následující příklad, který definuje třídu, která zaznamenává inicializaci a zničení objektů a pak definuje tři objekty, I1
a I2
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
Tento příklad ukazuje, jak a kdy objekty I1
, I2
a I3
jsou inicializovány a kdy jsou zničeny.
Existuje několik bodů, které je potřeba poznamenat o programu:
Za prvé, a
I2
jsou automaticky zničeny,I1
když tok řízení ukončí blok, ve kterém jsou definovány.Za druhé v jazyce C++ není nutné deklarovat objekty ani proměnné na začátku bloku. Tyto objekty jsou navíc inicializovány pouze v případě, že tok řízení dosáhne svých definic. (
I2
aI3
jsou příklady těchto definic.) Výstup ukazuje, kdy jsou inicializovány.Nakonec statické místní proměnné, jako
I3
je zachování jejich hodnot při spuštění programu, ale jsou zničeny při ukončení programu.
Viz také
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro