
Olá. Em uma das revisões de código, me deparei com a ideia de que muitos, e o que esconder, não que entendemos bem quando usar a palavra-chave estática . Neste artigo, gostaria de compartilhar meu conhecimento e informações sobre a palavra-chave estática . , , ++. /++ . , static ++, . ++, , , , .
static?
Static é uma palavra-chave C ++ usada para dar a um elemento uma característica especial. Para elementos estáticos, a memória é alocada apenas uma vez e esses elementos existem até que o programa termine. Todos esses elementos são armazenados não no heap ou na pilha, mas em segmentos de memória especiais chamados .data e .bss (dependendo se os dados estáticos são inicializados ou não). A imagem abaixo mostra um layout típico para memória de programa.

Onde é usado?
Abaixo está um diagrama de como e onde a estática é usada em um programa.

E agora tentarei descrever em detalhes tudo o que é mostrado no diagrama. Vai!
Variáveis estáticas dentro de uma função
Variáveis estáticas, quando usadas dentro de uma função, são inicializadas apenas uma vez e, então, mantêm seu valor. Essas variáveis estáticas são armazenadas em uma área de memória estática ( .data ou .bss ) em vez de na pilha, o que permite que o valor da variável seja armazenado e usado ao longo da vida do programa. Vamos dar uma olhada em dois programas quase idênticos e seu comportamento. A única diferença é que um usa uma variável estática e o outro não.
Primeiro programa:
#include <iostream>
void counter() {
static int count = 0; // 4
std::cout << count++;
}
int main() {
for (int i = 0; i < 10; ++i) {
counter();
}
return 0;
}
Resultado do programa:
0123456789
Segundo programa:
#include <iostream>
void counter() {
int count = 0; // 4
std::cout << count++;
}
int main() {
for (int i = 0; i < 10; ++i) {
counter();
}
return 0;
}
Resultado do programa:
000000000
Se você não usar static na linha 4 , a alocação de memória e a inicialização da contagem de variável ocorrerá toda vez que counter () for chamada e será destruída toda vez que a função for encerrada. Mas se tornarmos a variável estática, após a inicialização (a primeira vez que a função counter () é chamada ), a contagem terá como escopo o final da função main () e a variável manterá seu valor entre as chamadas para counter () .
Objetos de classe estática
Um objeto estático de uma classe tem as mesmas propriedades de uma variável estática regular descrita acima, ou seja, armazenado no segmento de memória .data ou .bss , criado na inicialização e destruído no término do programa e inicializado apenas uma vez. O objeto é inicializado normalmente - por meio do construtor da classe. Vamos considerar um exemplo com um objeto de classe estática.
#include <iostream>
class Base { // 3
public:
Base() { // 5
std::cout << "Constructor" << std::endl;
}
~Base() { // 8
std::cout << "Destructor" << std::endl;
}
};
void foo() {
static Base obj; // 14
} // 15
int main() {
foo(); // 18
std::cout << "End of main()" << std::endl;
return 0;
}
Resultado do programa:
Construtor
End of main ()
Destructor
3 Base ( 5) ( 8). . 14 obj Base. foo() 18.
- , , foo() 15, , .. . , , .
#include <iostream>
class Base {
public:
Base() {
std::cout << "Constructor" << std::endl;
}
~Base() {
std::cout << "Destructor" << std::endl;
}
};
void foo() {
Base obj;
} // 15
int main() {
foo();
std::cout << "End of main()" << std::endl;
return 0;
}
Se removermos a estática ao criar uma variável na função foo () , a destruição do objeto ocorrerá na linha 15 cada vez que a função for chamada. Neste caso, a saída do programa será bastante esperada para uma variável local com memória alocada na pilha:
Construtor
Destruidor
Fim do principal ()
Membros da classe estática
Em comparação com os casos de uso anteriores, os membros estáticos de uma classe são um pouco mais difíceis de entender. Vamos ver por quê. Suponha que temos o seguinte programa:
#include <iostream>
class A { // 3
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B { // 9
public:
B() { std::cout << "Constructor B" << std::endl; }
~B() { std::cout << "Destructor B" << std::endl; }
private:
static A a; // 15 ()
};
int main() {
B b; // 19
return 0;
}
Em nosso exemplo, criamos a classe A (linha 3) e a classe B (linha 9) com membros da classe estáticos ( linha 15 ). Assumimos que criar o objeto b na linha 19 criará o objeto a na linha 15 . Esse seria o caso se usássemos membros não estáticos da classe. Mas a saída do programa será a seguinte:
Construtor B
Destrutor B
A razão para esse comportamento é que os membros estáticos de uma classe não são inicializados com um construtor porque eles não dependem da inicialização do objeto. Essa. na linha 15 estamos apenas declarando o objeto, não definindo, pois a definição deve acontecer fora da classe usando o operador de resolução de escopo (: :) . Vamos definir um membros da classe B .
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B {
public:
B() { std::cout << "Constructor B" << std::endl; }
~B() { std::cout << "Destructor B" << std::endl; }
private:
static A a; // 15 ()
};
A B::a; // 18 ()
int main() {
B b;
return 0;
}
Agora, depois de definir nosso membro de classe estática na linha 18, podemos ver a seguinte saída do programa:
Construtor A
Construtor B
Destrutor B
Destrutor A
Deve ser lembrado que o membro da classe será o mesmo para todas as instâncias da classe B , ou seja, se criarmos três objetos da classe B , o construtor do membro da classe estática será chamado apenas uma vez. Aqui está um exemplo do que estou falando:
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B {
public:
B() { std::cout << "Constructor B" << count++ << std::endl; }
~B() { std::cout << "Destructor B" << --count << std::endl; }
private:
static A a; //
static int count; //
};
A B::a; //
int B::count = 1; //
int main() {
B b1, b2, b3;
return 0;
}
Resultado do programa:
Construtor A
Construtor B1
Construtor B2
Construtor B3
Destrutor B3
Destrutor B2
Destrutor B1
Destrutor A
Funções estáticas
As funções estáticas chegaram ao C ++ a partir de C. Por padrão, todas as funções em C são globais, e se você deseja criar duas funções com o mesmo nome em dois arquivos .c (.cpp) diferentes do mesmo projeto, você receberá um erro informando que esta função já definido ( erro fatal LNK1169: um ou mais símbolos multiplamente definidos encontrados ). Abaixo está uma lista de três arquivos de um programa.
// extend_math.cpp
int sum(int a, int b) {
int some_coefficient = 1;
return a + b + some_coefficient;
}
// math.cpp
int sum(int a, int b) {
return a + b;
}
// main.cpp
int sum(int, int); // declaration
int main() {
int result = sum(1, 2);
return 0;
}
Para corrigir esse problema, declararemos uma das funções como estática. Por exemplo, este:
// extend_math.cpp
static int sum(int a, int b) {
int some_coefficient = 1;
return a + b + some_coefficient;
}
, , . sum() math.cpp . , static , , , , , (.h).
, inline static, , . (.cpp), #include , . , , .. include .cpp .
- ()
Você pode usar uma função de membro estático sem criar um objeto da classe. As funções estáticas são acessadas usando o nome da classe e o operador de resolução de escopo (: :) . Ao usar uma função de membro estático, existem limitações, como:
- Dentro de uma função, você só pode acessar membros de dados estáticos, outras funções de membro estático e qualquer outra função fora da classe.
- As funções de membro estáticas têm o escopo da classe em que residem.
- Você não tem acesso a este ponteiro da classe, pois não criamos nenhum objeto para chamar esta função.
Vamos dar uma olhada no seguinte exemplo:
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
static void foo() { // 8
std::cout << "static foo()" << std::endl;
}
};
int main() {
A::foo(); // 14
return 0;
}
Na classe A, na linha 8 , temos uma função-membro estática foo () . Na linha 14 , chamamos a função usando o nome da classe e o operador de resolução de escopo, e obtemos a seguinte saída do programa:
foo estático ()
Na saída, você pode ver que não há criação de objeto e nenhum construtor / destruidor é chamado.
Se o método foo () não fosse estático, o compilador lançaria um erro na expressão na linha 14 , porque você precisa criar um objeto para acessar seus métodos não estáticos.
Conclusão
– « static , ». , , .
:
- , . , , , . , , .
- , , .. , . , , .. .
- static Singleton, , . , -. Singleton , , .
- Às vezes, para que uma função seja executada apenas uma vez sem armazenar o estado anterior em algum lugar do objeto, são utilizadas variáveis estáticas. Você pode ver um exemplo na seção "Variáveis estáticas dentro de uma função". Mas essa não é uma abordagem muito boa e pode levar a muitas horas de caça a bugs se você estiver usando multithreading.
- Na prática, os programadores C ++ costumam usar funções de membro estáticas como uma alternativa às funções regulares que não requerem a criação de um objeto para execução.
Espero que tenha gostado do meu artigo sobre a palavra-chave estática em C ++. Eu ficaria feliz com qualquer crítica e conselho. Obrigado a todos!