Поделиться через


Классы хранилищ

Класс хранилища в контексте объявлений переменных C++ — это описатель типа, который управляет временем существования, компоновкой и расположением памяти объектов. Каждый объект может иметь только один класс хранения. Переменные, определенные в блоке, имеют автоматическое хранилище, если иное не указано с помощью externstaticописателей или thread_local описателей. Автоматические объекты и переменные не имеют связи; Они не видны коду за пределами блока. Память выделяется для них автоматически при входе в блок и удаляется при выходе блока.

Примечания.

  • Ключевое слово mutable может считаться описатель класса хранилища. Однако он доступен только в списке членов определения класса.

  • Visual Studio 2010 и более поздних версий:auto ключевое слово больше не является описателями класса хранилища C++, а register ключевое слово устарели. Visual Studio 2017 версии 15.7 и более поздних версий: (доступно в /std:c++17 режиме и более поздних версиях): register ключевое слово удаляется из языка C++. Его использование вызывает диагностическое сообщение:

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

static Ключевое слово можно использовать для объявления переменных и функций в глобальных область, пространства имен область и класса область. Статические переменные также могут быть объявлены в локальной области видимости.

Статическая длительность означает, что объект или переменная выделяется при запуске программы и освобождается при ее завершении. Внешняя компоновка означает, что имя переменной отображается вне файла, в котором объявлена переменная. И наоборот, внутренняя компоновка означает, что имя не отображается за пределами файла, в котором объявлена переменная. По умолчанию объект или переменная, определенные в глобальном пространстве имен, имеют статическую длительность и внешнюю компоновку. Ключевое слово static можно использовать в следующих ситуациях.

  1. При объявлении переменной или функции в файле область (глобальные и/или или область пространства имен) ключевое слово указывает, static что переменная или функция имеет внутреннюю связь. При объявлении переменной она имеет статическую длительность и компилятор инициализирует ее со значением 0, если не указано другое значение.

  2. При объявлении переменной в функции ключевое слово указывает, static что переменная сохраняет состояние между вызовами этой функции.

  3. При объявлении члена данных в объявлении static класса ключевое слово указывает, что одна копия элемента разделяется всеми экземплярами класса. Элемент static данных должен быть определен в файле область. Целочисленный элемент данных, объявленный как const static инициализатор.

  4. При объявлении функции-члена в объявлении static класса ключевое слово указывает, что функция совместно используется всеми экземплярами класса. Функция-член static не может получить доступ к члену экземпляра, так как функция не имеет неявного this указателя. Чтобы получить доступ к члену экземпляра, объявите функцию с параметром, указывающим на экземпляр или ссылкой.

  5. Вы не можете объявить члены объекта union как static. Однако глобально объявленный анонимный union должен быть явно объявлен static.

В этом примере показано, как переменная, объявленная static в функции, сохраняет состояние между вызовами этой функции.

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

В этом примере показано использование 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

В следующем примере показана локальная переменная, объявленная static в функции-члене. Переменная static доступна для всей программы. Все экземпляры типа используют одну и ту же копию переменной 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

Начиная с C++11, static инициализация локальных переменных гарантируется потокобезопасной. Эта функция иногда называется магической статики. Однако в многопоточном приложении все последующие назначения должны быть синхронизированы. Функцию инициализации, безопасную для потоков, можно отключить с помощью флага /Zc:threadSafeInit- , чтобы избежать зависимостей от CRT.

extern

Объекты и переменные, объявленные как extern объявление объекта, определенного в другой единице перевода или в заключенном область как наличие внешней компоновки. Дополнительные сведения см. в разделах extern и единицах перевода и компоновке.

thread_local (C++11)

Переменная, объявленная с thread_local помощью описателя, доступна только в потоке, на котором она создана. Переменная создается при создании потока и уничтожается при уничтожении потока. Каждый поток имеет свою собственную копию переменной. В Windows thread_local функционально эквивалентен атрибуту майкрософт __declspec( thread ) .

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

Сведения о описателях thread_local :

  • Динамически инициализированные локальные переменные потока в библиотеках DLL могут быть неправильно инициализированы во всех вызывающих потоках. Дополнительные сведения см. в разделе thread.

  • Описатель thread_local может сочетаться с static или extern.

  • Можно применять thread_local только к объявлениям и определениям данных; thread_local нельзя использовать в объявлениях или определениях функций.

  • Можно указать thread_local только элементы данных со статическим сроком хранения, включая глобальные объекты данных (как и ), локальные статические объекты, так static и externстатические элементы данных классов. Любая локальная переменная, объявленная thread_local неявно, является неявной, если ни один другой класс хранилища не указан, иными словами, в блоке область thread_local эквивалентенthread_local static.

  • thread_local следует использовать для объявления и определения локального объекта потока независимо от того, выполняются ли объявление и определение в одном файле или в отдельных файлах.

  • Мы не рекомендуем использовать thread_local переменные с std::launch::async. Дополнительные сведения см. в разделе <future> "Функции".

В Windows thread_local функционально эквивалентен __declspec(thread) за исключением того, что *__declspec(thread)* можно применить к определению типа и является допустимым в коде C. По возможности используйте thread_local , так как она является частью стандарта C++ и поэтому является более переносимой.

регистрация

Visual Studio 2017 версии 15.3 и более поздних версий (доступно в /std:c++17 режиме и более поздних версиях): register ключевое слово больше не является поддерживаемым классом хранения. Его использование приводит к диагностике. Ключевое слово по-прежнему зарезервированы в стандарте для дальнейшего использования.

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

Пример: автоматическая и статическая инициализация

Локальные автоматически создаваемые объекты или переменные инициализируются каждый раз, когда поток элемента управления достигает их определения. Локальные статические объекты или переменные инициализируются, когда поток элемента управления достигает их определения в первый раз.

Рассмотрим следующий пример, в котором определяется класс, который регистрирует инициализацию и удаление объектов, а затем определяет три объекта: I1, 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

В этом примере показано, как и когда объекты I1I2инициализированы и I3 когда они уничтожены.

Существует несколько моментов, которые следует отметить о программе:

  • Во-первых, I1 и I2 автоматически уничтожаются при выходе потока управления из блока, в котором они определены.

  • Во-вторых, в C++не требуется объявлять объекты или переменные в начале блока. Более того, эти объекты инициализируются, только если поток элемента управления достигает их определения. (I2 и I3 являются примерами таких определений.) Выходные данные показывают, когда они инициализированы.

  • Наконец, статические локальные переменные, такие как I3 сохранение их значений во время выполнения программы, но уничтожаются по мере завершения программы.

См. также

Объявления и определения