Classes de armazenamento
Uma classe de armazenamento no contexto de declarações de variáveis C++ é um especificador de tipo que controla o tempo de vida, a vinculação e a localização da memória dos objetos. Um determinado objeto pode ter apenas uma classe de armazenamento. As variáveis definidas em um bloco têm armazenamento automático, a menos que especificado de outra forma usando os especificadores extern
, static
ou thread_local
. Objetos e variáveis automáticos não têm vinculação; eles não são visíveis para codificar fora do bloco. Para eles, a memória é alocada automaticamente quando a execução entra no bloco e é desalocada quando o bloco é encerrado.
Observações
A palavra-chave
mutable
pode ser considerada um especificador de classe de armazenamento. No entanto, só está disponível na lista de membros de uma definição de classe.Visual Studio 2010 e posterior: a palavra-chave
auto
não é mais um especificador de classe de armazenamento C++ e a palavra-chaveregister
está obsoleta. Visual Studio 2017 versão 15.7 e posterior: (disponível no modo/std:c++17
e posterior): a palavra-chaveregister
foi removida da linguagem C++. Seu uso gera 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 palavra-chave static
pode ser usada para declarar variáveis e funções em escopo global, escopo de namespace e escopo de classe. Variáveis estáticas também podem ser declaradas no escopo local.
Duração estática significa que o objeto ou a variável são alocados quando o programa inicia e desalocados quando o programa termina. Vinculação externa significa que o nome da variável é visível de fora do arquivo onde a variável é declarada. Por sua vez, a vinculaçã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 vinculação externa. A palavra-chave static
pode ser usada nas seguintes situações.
Ao declarar uma variável ou função no escopo do arquivo (escopo global e/ou de namespace), a palavra-chave
static
especifica que a variável ou função possui vinculação interna. Ao declarar uma variável, a variável tem duração estática, e o compilador a inicializa com o valor 0 a menos que você especifique outro valor.Quando você declara uma variável em uma função, a palavra-chave
static
especifica que a variável retém seu estado entre as chamadas para essa função.Quando você declara um membro de dados em uma declaração de classe, a palavra-chave
static
especifica que uma cópia do membro é compartilhada por todas as instâncias da classe. Um membro de dadosstatic
deve ser definido no escopo do arquivo. Um membro de dados integral que você declara comoconst static
pode ter um inicializador.Quando você declara uma função membro em uma declaração de classe, a palavra-chave
static
especifica que a função é compartilhada por todas as instâncias da classe. Uma função membrostatic
não pode acessar um membro de instância porque a função não tem um ponteirothis
implícito. Para acessar um membro de instância, declare a função com um parâmetro que seja um ponteiro ou referência de instância.Você não pode declarar os membros de um
union
comostatic
. No entanto, umaunion
anônima declarada globalmente deve ser declarada explicitamente comostatic
.
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 variável static
está disponível para todo o programa; todas as instâncias do tipo compartilham a mesma cópia da variável 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
A partir do C++11, uma inicialização de variável local static
é garantida como thread-safe. Esse recurso às vezes é chamado de estática mágica. No entanto, em um aplicativo multithread, todas as atribuições subsequentes devem ser sincronizadas. O recurso de inicialização estática thread-safe pode ser desabilitado usando o sinalizador /Zc:threadSafeInit-
para evitar a dependência do CRT.
extern
Objetos e variáveis declarados como extern
declaram um objeto definido em outra unidade de tradução ou em um escopo delimitador como tendo vinculaçã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 especificador thread_local
é acessível apenas no thread em que foi 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 __declspec( thread )
específico 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;
}
Observações sobre o especificador thread_local
:
Variáveis locais de thread inicializadas dinamicamente em DLLs podem não ser inicializadas corretamente em todos os threads de chamada. Para obter mais informações, consulte
thread
.O especificador
thread_local
pode ser combinado comstatic
ouextern
.Você pode aplicar
thread_local
somente 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
apenas em itens de dados com duração de armazenamento estático, que inclui objetos de dados globais (tantostatic
quantoextern
), objetos estáticos locais e membros de dados estáticos de classes. Qualquer variável local declaradathread_local
é implicitamente estática se nenhuma outra classe de armazenamento for fornecida; em outras palavras, no escopo do blocothread_local
é equivalente athread_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 ocorrerem no mesmo arquivo ou em arquivos separados.Não recomendamos que você use variáveis de
thread_local
comstd::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 em código C. Sempre que possível, use thread_local
porque faz parte do padrão C++ e, portanto, é mais portátil.
register
Visual Studio 2017 versão 15.3 e posterior (disponível no modo /std:c++17
e posterior): a palavra-chave register
não é mais uma classe de armazenamento compatível. 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 uma variável local automática são inicializados cada vez que o fluxo de controle alcança sua definição. Um objeto ou uma variável local estática são inicializados na primeira vez que o fluxo de controle alcança sua definição.
Considere o exemplo a seguir, que define uma classe que registra a inicialização e a destruição de objetos e depois define três objetos, I1
, I2
e 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á diversos pontos a serem observados sobre o programa:
Primeiro,
I1
eI2
são destruídos automaticamente quando o fluxo de controle sai do bloco no qual estão definidos.Segundo, 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
eI3
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
retêm seus valores enquanto o programa é executado, mas são destruídas quando o programa termina.