Classes de armazenamento

Uma classe de armazenamento no contexto de declarações de variáveis C++ é um especificador de tipo que governa o tempo de vida, a ligação e o local da memória de objetos. Um determinado objeto pode ter apenas uma classe de armazenamento. As variáveis definidas dentro de um bloco têm armazenamento automático, a menos que especificado de outra forma usando o extern, staticou thread_local especificadores. Objetos e variáveis automáticas não têm ligação; eles não são visíveis para codificar fora do bloco. A memória é alocada para eles automaticamente quando a execução entra no bloco e é desalocada quando o bloco é encerrado.

Observações

  • A mutable palavra-chave pode ser considerada um especificador de classe de armazenamento. No entanto, ele só está disponível na lista de membros de uma definição de classe.

  • Visual Studio 2010 e posterior: A auto palavra-chave não é mais um especificador de classe de armazenamento C++ e a register palavra-chave foi preterida. Visual Studio 2017 versão 15.7 e posterior: (disponível no /std:c++17 modo e posterior): A register palavra-chave é removida da linguagem C++. Seu uso causa uma mensagem de diagnóstico:

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

A static palavra-chave pode ser usada para declarar variáveis e funções no escopo global, escopo do namespace e escopo da classe. As variáveis estáticas também podem ser declaradas no âmbito local.

Duração estática significa que o objeto ou variável é alocado quando o programa é iniciado e é desalocado quando o programa termina. Ligação externa significa que o nome da variável é visível de fora do arquivo onde a variável é declarada. Por outro lado, a ligação interna significa que o nome não é visível fora do arquivo onde a variável é declarada. Por padrão, um objeto ou variável definido no namespace global tem duração estática e ligação externa. A static palavra-chave pode ser usada nas seguintes situações.

  1. Quando você declara uma variável ou função no escopo do arquivo (escopo global e/ou namespace), a static palavra-chave especifica que a variável ou função tem ligação interna. Quando você declara uma variável, a variável tem duração estática e o compilador a inicializa como 0, a menos que você especifique outro valor.

  2. Quando você declara uma variável em uma função, a static palavra-chave especifica que a variável mantém seu estado entre chamadas para essa função.

  3. Quando você declara um membro de dados em uma declaração de classe, a static palavra-chave especifica que uma cópia do membro é compartilhada por todas as instâncias da classe. Um static membro de dados deve ser definido no escopo do arquivo. Um membro de dados integral que você declara como const static pode ter um inicializador.

  4. Quando você declara uma função de membro em uma declaração de classe, a static palavra-chave especifica que a função é compartilhada por todas as instâncias da classe. Uma static função de membro não pode acessar um membro da instância porque a função não tem um ponteiro implícito this . Para acessar um membro da instância, declare a função com um parâmetro que seja um ponteiro ou referência da instância.

  5. Não é possível declarar os membros de um union como static. No entanto, um globalmente declarado anónimo union deve ser explicitamente declarado static.

Este exemplo mostra como uma variável declarada static em uma função mantém seu estado entre chamadas para essa função.

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

Este exemplo mostra o uso de static em uma classe.

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

O exemplo a seguir mostra uma variável local declarada static em uma função membro. A static variável está disponível para todo o programa, todas as instâncias do tipo compartilham a mesma cópia da static variável.

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

A partir do C++11, uma inicialização de static variável local tem a garantia de ser thread-safe. Este recurso é às vezes chamado de estática mágica. No entanto, em um aplicativo multithreaded, todas as atribuições subsequentes devem ser sincronizadas. O recurso de inicialização estática thread-safe pode ser desativado usando o /Zc:threadSafeInit- sinalizador para evitar uma dependência do CRT.

extern

Objetos e variáveis declarados como extern declaram um objeto que é definido em outra unidade de tradução ou em um escopo de inclusão como tendo ligação externa. Para obter mais informações, consulte extern e Unidades de tradução e vinculação.

thread_local (C++11)

Uma variável declarada com o thread_local especificador é acessível somente no thread no qual é criada. A variável é criada quando o thread é criado e é destruída quando o thread é destruído. Cada thread tem sua própria cópia da variável. No Windows, thread_local é funcionalmente equivalente ao atributo específico __declspec( thread ) da 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;
}

Aspetos a ter em conta sobre o thread_local especificador:

  • Variáveis thread-local inicializadas dinamicamente em DLLs podem não ser inicializadas corretamente em todos os threads de chamada. Para obter mais informações, consulte thread.

  • O thread_local especificador pode ser combinado com static ou extern.

  • Você pode aplicar thread_local apenas a declarações e definições de dados, thread_local não pode ser usado em declarações ou definições de função.

  • Você pode especificar thread_local somente em itens de dados com duração de armazenamento estático, o que inclui objetos de dados globais (ambos static e extern), objetos estáticos locais e membros de dados estáticos de classes. Qualquer variável local declarada thread_local é implicitamente estática se nenhuma outra classe de armazenamento for fornecida, ou seja, no escopo thread_local do bloco é equivalente a thread_local static.

  • Você deve especificar thread_local para a declaração e a definição de um objeto local de thread, se a declaração e a definição ocorrem no mesmo arquivo ou em arquivos separados.

  • Não recomendamos que você use thread_local variáveis com std::launch::async. Para obter mais informações, consulte <future> funções.

No Windows, thread_local é funcionalmente equivalente a __declspec(thread) exceto que *__declspec(thread)* pode ser aplicado a uma definição de tipo e é válido no código C. Sempre que possível, use thread_local porque faz parte do padrão C++ e, portanto, é mais portátil.

registo

Visual Studio 2017 versão 15.3 e posterior (disponível no /std:c++17 modo e posterior): A register palavra-chave não é mais uma classe de armazenamento suportada. Seu uso causa um diagnóstico. A palavra-chave ainda está reservada no padrão para uso futuro.

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

Exemplo: inicialização automática vs. estática

Um objeto ou variável automática local é inicializado toda vez que o fluxo de controle atinge sua definição. Um objeto estático local ou variável é inicializado na primeira vez que o fluxo de controle atinge sua definição.

Considere o exemplo a seguir, que define uma classe que registra a inicialização e a destruição de objetos e, em seguida, define três objetos, I1, I2e 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

Este exemplo demonstra como e quando os objetos I1, I2, e I3 são inicializados e quando são destruídos.

Há vários pontos a observar sobre o programa:

  • Primeiro, I1 e I2 são automaticamente destruídos quando o fluxo de controle sai do bloco no qual eles estão definidos.

  • Em segundo lugar, em C++, não é necessário declarar objetos ou variáveis no início de um bloco. Além disso, esses objetos são inicializados somente quando o fluxo de controle atinge suas definições. I2( e I3 são exemplos de tais definições.) A saída mostra exatamente quando eles são inicializados.

  • Finalmente, variáveis locais estáticas, como I3 reter seus valores enquanto o programa é executado, mas são destruídas quando o programa termina.

Ver também

Declarações e definições