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í externspecifikátorů , staticnebo 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++ a register 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.

  1. 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.

  2. 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.

  3. 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 jako const static může mít inicializátor.

  4. 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.

  5. Nemůžete deklarovat členy union jako static. Globálně deklarovaný anonymní union však musí být explicitně deklarován static.

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 s static nebo extern.

  • 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 (a staticextern), 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 rozsahu thread_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é s std::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, I1a I2I3:

// 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, I2a 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 a I3 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é

Deklarace a definice