A palavra-chave estática e seus vários usos em C ++

195

A palavra static- chave é aquela que tem vários significados em C ++ que eu acho muito confusos e que nunca consigo entender como é que realmente deveria funcionar.

Pelo que entendi, existe a staticduração do armazenamento, o que significa que dura a vida útil do programa no caso de um global, mas quando você está falando sobre um local, significa que ele é zero inicializado por padrão.

O padrão C ++ diz isso para membros de dados da classe com a palavra static- chave :

3.7.1 Duração estática do armazenamento [basic.stc.static]

3 A palavra-chave static pode ser usada para declarar uma variável local com duração de armazenamento estático.

4 A palavra-chave static aplicada a um membro de dados da classe em uma definição de classe fornece a duração do armazenamento estático do membro de dados.

O que isso significa com variável local ? Essa é uma variável local de função? Porque também há que quando você declara uma função local como staticsendo inicializada apenas uma vez, na primeira vez em que entra nessa função.

Ele também fala apenas sobre a duração do armazenamento em relação aos membros da classe, que tal não ser específico da instância, isso também é uma propriedade de staticno? Ou é a duração do armazenamento?

Agora, e o caso com statico escopo do arquivo? Todas as variáveis ​​globais são consideradas como tendo duração de armazenamento estático por padrão? O seguinte (da seção 3.7.1) parece indicar isso:

1 Todas as variáveis ​​que não possuem duração de armazenamento dinâmico, não possuem duração de armazenamento de encadeamento e não são locais têm duração de armazenamento estático. O armazenamento para essas entidades deve durar a duração do programa (3.6.2, 3.6.3)

Como se staticrelaciona com a ligação de uma variável?

Essa staticpalavra-chave inteira é absolutamente confusa. Alguém pode esclarecer os diferentes usos do inglês e também me dizer quando inicializar um staticaluno?

Tony o leão
fonte

Respostas:

147

Variáveis:

staticexistem variáveis ​​para o "tempo de vida" da unidade de tradução em que está definida e:

  • Se estiver no escopo de um namespace (ou seja, fora de funções e classes), não poderá ser acessado de nenhuma outra unidade de tradução. Isso é conhecido como "ligação interna" ou "duração do armazenamento estático". (Não faça isso nos cabeçalhos, exceto em constexpr. Mais alguma coisa e você acaba com uma variável separada em cada unidade de tradução, o que é uma loucura confusa)
  • Se for uma variável em uma função , ela não poderá ser acessada de fora da função, como qualquer outra variável local. (este é o local que eles mencionaram)
  • os membros da classe não têm escopo restrito devido a static, mas podem ser endereçados a partir da classe e de uma instância (como std::string::npos). [Nota: você pode declarar membros estáticos em uma classe, mas eles geralmente ainda devem ser definidos em uma unidade de tradução (arquivo cpp) e, como tal, existe apenas um por classe]

locais como código:

static std::string namespaceScope = "Hello";
void foo() {
    static std::string functionScope= "World";
}
struct A {
   static std::string classScope = "!";
};

Antes de qualquer função em uma unidade de conversão ser executada (possivelmente após o maininício da execução), as variáveis ​​com duração de armazenamento estático (escopo do namespace) nessa unidade de conversão serão "inicializadas constantemente" (para constexpronde for possível, ou zero caso contrário) e, em seguida, não- os habitantes locais são "inicializados dinamicamente" corretamente na ordem em que são definidos na unidade de tradução (para coisas como std::string="HI";essa não são constexpr). Por fim, as estatísticas da função local serão inicializadas na primeira vez em que a execução "atingir" a linha em que são declaradas. Todas as staticvariáveis ​​são destruídas na ordem inversa da inicialização.

A maneira mais fácil de corrigir tudo isso é transformar todas as variáveis ​​estáticas que não foram constexprinicializadas em locais estáticos de função, o que garante que todas as suas estatísticas / globais sejam inicializadas corretamente quando você tentar usá-las, independentemente do que seja, impedindo a inicialização estática ordem fiasco .

T& get_global() {
    static T global = initial_value();
    return global;
}

Tenha cuidado, porque quando a especificação diz que variáveis ​​de escopo de espaço para nome têm "duração de armazenamento estático" por padrão, elas significam o bit "tempo de vida da unidade de tradução", mas isso não significa que não possa ser acessado fora do arquivo.

Funções

Significativamente mais direto, staticgeralmente é usado como uma função de membro da classe e apenas raramente é usado para uma função independente.

Uma função de membro estática difere de uma função de membro regular, pois pode ser chamada sem uma instância de uma classe e, como não possui instância, não pode acessar membros não estáticos da classe. As variáveis ​​estáticas são úteis quando você deseja ter uma função para uma classe que definitivamente não se refere absolutamente a nenhum membro da instância ou para gerenciar staticvariáveis ​​de membro.

struct A {
    A() {++A_count;}
    A(const A&) {++A_count;}
    A(A&&) {++A_count;}
    ~A() {--A_count;}

    static int get_count() {return A_count;}
private:
    static int A_count;
}

int main() {
    A var;

    int c0 = var.get_count(); //some compilers give a warning, but it's ok.
    int c1 = A::get_count(); //normal way
}

Uma staticfunção livre significa que a função não será referida por nenhuma outra unidade de conversão e, portanto, o vinculador pode ignorá-la completamente. Isso tem um pequeno número de propósitos:

  • Pode ser usado em um arquivo cpp para garantir que a função nunca seja usada em nenhum outro arquivo.
  • Pode ser colocado em um cabeçalho e cada arquivo terá sua própria cópia da função. Não é útil, pois o inline faz praticamente a mesma coisa.
  • Acelera o tempo do link reduzindo o trabalho
  • Pode colocar uma função com o mesmo nome em cada unidade de tradução e todos podem fazer coisas diferentes. Por exemplo, você pode colocar um static void log(const char*) {}em cada arquivo cpp, e eles podem logar de maneira diferente.
Mooing Duck
fonte
1
E os alunos? Não é um terceiro caso separado?
Étienne
4
@ Etienne - membros de dados de classe estática são os mesmos que variáveis ​​globais estáticas, exceto que você pode acessá-los de outras unidades de conversão, e qualquer acesso (exceto funções de membro) deve especificar o classname::escopo. As funções de membro de classe estática são como funções globais, mas com escopo definido para a classe, ou como membros normais, mas sem this(isso não é uma escolha - essas duas devem ser equivalentes).
precisa saber é o seguinte
1
@LuchianGrigore: enquanto eu entendo o seu ponto, não sei ao certo o que usar.
precisa
1
@ Steve314: Entendo o que você quer dizer, mas ao lidar com um termo tão terrivelmente sobrecarregado como estático , gostaria que fôssemos todos um pouco mais cuidadosos. Em particular, todas as variáveis ​​globais (realmente no nível de namespace) têm duração estática; portanto, a adição de estática em variáveis ​​globais estáticas pode ser entendida como namespace A { static int x; }, o que significa ligação interna e é muito diferente do comportamento dos membros de dados da classe estática .
David Rodríguez - dribeas
1
"Se estiver no escopo de um espaço para nome, não poderá ser acessado em nenhuma outra unidade de tradução ..." O que você quer dizer se estiver no escopo de um espaço para nome? Não é sempre esse o caso, você poderia dar um exemplo e um contra-exemplo?
AturSams
66

A duração estática do armazenamento significa que a variável reside no mesmo local na memória durante a vida útil do programa.

A ligação é ortogonal a isso.

Eu acho que essa é a distinção mais importante que você pode fazer. Entenda isso e o resto, além de lembrá-lo, deve ser fácil (não abordando diretamente o @Tony, mas quem possa ler isso no futuro).

A palavra-chave staticpode ser usada para indicar ligação interna e armazenamento estático, mas, em essência, são diferentes.

O que isso significa com variável local? Essa é uma variável local de função?

Sim. Independentemente de quando a variável for inicializada (na primeira chamada da função e quando o caminho de execução atingir o ponto de declaração), ela residirá no mesmo local na memória durante a vida útil do programa. Nesse caso, staticfornece armazenamento estático.

Agora, e o caso com escopo estático e de arquivo? Todas as variáveis ​​globais são consideradas como tendo duração de armazenamento estático por padrão?

Sim, todos os globais têm, por definição, duração de armazenamento estático (agora que esclarecemos o que isso significa). Mas as variáveis ​​com escopo de namespace não são declaradas com static, porque isso lhes daria ligação interna, portanto, uma variável por unidade de tradução.

Como estática se relaciona com a ligação de uma variável?

Ele fornece ligação interna a variáveis ​​com escopo de espaço para nome. Ele fornece aos membros e variáveis ​​locais a duração do armazenamento estático.

Vamos expandir tudo isso:

//

static int x; //internal linkage
              //non-static storage - each translation unit will have its own copy of x
              //NOT A TRUE GLOBAL!

int y;        //static storage duration (can be used with extern)
              //actual global
              //external linkage
struct X
{
   static int x;     //static storage duration - shared between class instances 
};

void foo()
{
   static int x;     //static storage duration - shared between calls
}

Toda essa palavra-chave estática é totalmente confusa

Definitivamente, a menos que você esteja familiarizado com isso. :) Tentando evitar adicionar novas palavras-chave ao idioma, o comitê reutilizou esta, IMO, para esse efeito - confusão. É usado para significar coisas diferentes (posso dizer, provavelmente coisas opostas).

Luchian Grigore
fonte
1
Deixe-me ver se entendi - você está dizendo que, quando digo static int xno escopo do espaço para nome, isso fornece armazenamento não estático ?
Michael Hagar
30

Para esclarecer a questão, prefiro categorizar o uso da palavra-chave 'estática' de três formas diferentes:

(UMA). variáveis

(B) funções

(C) variáveis-membro / funções de classes

a explicação a seguir para cada um dos subtítulos:

(A) palavra-chave 'estática' para variáveis

Este pode ser um pouco complicado, no entanto, se explicado e entendido corretamente, é bem direto.

Para explicar isso, primeiro é realmente útil saber sobre o escopo, a duração e a ligação das variáveis, sem as quais as coisas sempre são difíceis de ver através do conceito obscuro da palavra-chave staic

1. Escopo : determina onde, no arquivo, a variável está acessível. Pode ser de dois tipos: (i) Local ou Escopo do Bloco . (ii) Escopo Global

2. Duração : determina quando uma variável é criada e destruída. Novamente, é de dois tipos: (i) Duração automática do armazenamento (para variáveis ​​com escopo Local ou Bloco). (ii) Duração de armazenamento estático (para variáveis ​​com escopo global ou variáveis ​​locais (em uma função ou a em um bloco de código) com especificador estático ).

3. Vinculação : determina se uma variável pode ser acessada (ou vinculada) em outro arquivo. Novamente (e felizmente), ele é de dois tipos: (i) Ligação interna (para variáveis ​​com escopo de bloco e Escopo global / Escopo de arquivo / Escopo de espaço para nome global) (ii) Ligação externa (para variáveis ​​com apenas escopo global / Escopo de arquivo / Escopo do Namespace Global)

Vamos consultar um exemplo abaixo para entender melhor as variáveis ​​globais e locais simples (sem variáveis ​​locais com duração de armazenamento estático):

//main file
#include <iostream>

int global_var1; //has global scope
const global_var2(1.618); //has global scope

int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is 
//  executed and destroyed, when main goes out of scope
 int local_var1(23);
 const double local_var2(3.14);

 {
/* this is yet another block, all variables declared within this block are 
 have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e, 
/*they are created at the point of definition within this block,
 and destroyed as soon as this block ends */
   char block_char1;
   int local_var1(32) //NOTE: this has been re-declared within the block, 
//it shadows the local_var1 declared outside

 std::cout << local_var1 <<"\n"; //prints 32

  }//end of block
  //local_var1 declared inside goes out of scope

 std::cout << local_var1 << "\n"; //prints 23

 global_var1 = 29; //global_var1 has been declared outside main (global scope)
 std::cout << global_var1 << "\n"; //prints 29
 std::cout << global_var2 << "\n"; //prints 1.618

 return 0;
}  //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates 
//(in this case program ends with end of main, so both local and global
//variable go out of scope together

Agora vem o conceito de ligação. Quando uma variável global definida em um arquivo se destina a ser usada em outro arquivo, a ligação da variável desempenha um papel importante.

A ligação de variáveis ​​globais é especificada pelas palavras-chave: (i) estática e (ii) externa

(Agora você obtém a explicação)

A palavra-chave estática pode ser aplicada a variáveis ​​com escopo local e global e, nos dois casos, elas significam coisas diferentes. Primeiro, explicarei o uso da palavra-chave 'estática' em variáveis ​​com escopo global (onde também esclareço o uso da palavra-chave 'extern') e, posteriormente, para aquelas com escopo local.

1. Palavra-chave estática para variáveis ​​com escopo global

Variáveis ​​globais têm duração estática, o que significa que não ficam fora do escopo quando um bloco de código específico (por exemplo, main ()) no qual é usado termina. Dependendo da ligação, eles podem ser acessados ​​apenas dentro do mesmo arquivo em que são declarados (para variável global estática) ou fora do arquivo, mesmo fora do arquivo em que foram declarados (variáveis ​​globais do tipo externo)

No caso de uma variável global com especificador externo, e se essa variável estiver sendo acessada fora do arquivo em que foi inicializada, ela deve ser declarada para frente no arquivo em que está sendo usada, assim como uma função deve ser encaminhada declarado se sua definição estiver em um arquivo diferente de onde está sendo usado.

Por outro lado, se a variável global tiver uma palavra-chave estática, ela não poderá ser usada em um arquivo fora do qual foi declarada.

(veja o exemplo abaixo para esclarecimentos)

por exemplo:

//main2.cpp
 static int global_var3 = 23;  /*static global variable, cannot be                            
                                accessed in anyother file */
 extern double global_var4 = 71; /*can be accessed outside this file                  linked to main2.cpp */
 int main() { return 0; }

main3.cpp

//main3.cpp
#include <iostream>

int main()
{
   extern int gloabl_var4; /*this variable refers to the gloabal_var4
                            defined in the main2.cpp file */
  std::cout << global_var4 << "\n"; //prints 71;

  return 0;
}

agora qualquer variável em c ++ pode ser uma const ou uma não-const e, para cada 'constância', obtemos dois casos de ligação c ++ padrão, caso nenhuma seja especificada:

(i) Se uma variável global não for const, seu vínculo é externo por padrão , ou seja, a variável global não const pode ser acessada em outro arquivo .cpp por declaração direta usando a palavra-chave extern (em outras palavras, non const global variáveis ​​têm ligação externa (com duração estática, é claro)). Também o uso da palavra-chave externa no arquivo original em que foi definida é redundante. Nesse caso, para tornar uma variável global não const inacessível ao arquivo externo, use o especificador 'estático' antes do tipo da variável .

(ii) Se uma variável global é const, seu vínculo é estático por padrão , ou seja, uma variável global const não pode ser acessada em um arquivo que não seja o local onde está definido (em outras palavras, variáveis ​​globais const têm vínculo interno (com duração estática claro)). Também o uso da palavra-chave estática para impedir que uma variável global const seja acessada em outro arquivo é redundante. Aqui, para fazer uma variável global const ter uma ligação externa, use o especificador 'extern' antes do tipo da variável

Aqui está um resumo para variáveis ​​de escopo global com várias ligações

//globalVariables1.cpp 

// defining uninitialized vairbles
int globalVar1; //  uninitialized global variable with external linkage 
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared

A seguir, investigamos como as variáveis ​​globais acima se comportam quando acessadas em um arquivo diferente.

//using_globalVariables1.cpp (eg for the usage of global variables above)

// Forward declaration via extern keyword:
 extern int globalVar1; // correct since globalVar1 is not a const or static
 extern int globalVar2; //incorrect since globalVar2 has internal linkage
 extern const int globalVar4; /* incorrect since globalVar4 has no extern 
                         specifier, limited to internal linkage by
                         default (static specifier for const variables) */
 extern const double globalVar5; /*correct since in the previous file, it 
                           has extern specifier, no need to initialize the
                       const variable here, since it has already been
                       legitimately defined perviously */

2. Palavra-chave estática para variáveis ​​com escopo local

Atualizações (agosto de 2019) na palavra-chave estática para variáveis ​​no escopo local

Isso ainda pode ser subdividido em duas categorias:

(i) palavra-chave estática para variáveis ​​dentro de um bloco de função e (ii) palavra-chave estática para variáveis ​​dentro de um bloco local sem nome.

(i) palavra-chave estática para variáveis ​​dentro de um bloco de funções.

Mencionei anteriormente que variáveis ​​com escopo local têm duração automática, ou seja, passam a existir quando o bloco é inserido (seja um bloco normal, seja um bloco funcional) e deixam de existir quando o bloco termina, para resumir a história, variáveis com escopo local têm duração automática e variáveis ​​de duração automática (e objetos) não têm vínculo, o que significa que não são visíveis fora do bloco de código.

Se o especificador estático for aplicado a uma variável local dentro de um bloco de função, ele altera a duração da variável de automática para estática e seu tempo de vida é toda a duração do programa, o que significa que ele possui um local de memória fixo e seu valor é inicializado apenas uma vez antes da inicialização do programa, conforme mencionado na referência cpp (a inicialização não deve ser confundida com a atribuição)

vamos dar uma olhada em um exemplo.

//localVarDemo1.cpp    
 int localNextID()
{
  int tempID = 1;  //tempID created here
  return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here :-)


int main()
{
  int employeeID1 = localNextID();  //employeeID1 = 1
  int employeeID2 = localNextID();  // employeeID2 = 1 again (not desired)
  int employeeID3 = newNextID(); //employeeID3 = 0;
  int employeeID4 = newNextID(); //employeeID4 = 1;
  int employeeID5 = newNextID(); //employeeID5 = 2;
  return 0;
}

Observando o critério acima para variáveis ​​locais estáticas e variáveis ​​globais estáticas, pode-se tentar perguntar qual a diferença entre elas. Enquanto as variáveis globais estão acessíveis em qualquer ponto dentro do código (na mesma, bem como unidade de conversão diferente dependendo da const -ness e externo -ness), uma variável estático definido dentro de um bloco de função não é directamente acessível. A variável deve ser retornada pelo valor ou referência da função. Vamos demonstrar isso por um exemplo:

//localVarDemo2.cpp 

//static storage duration with global scope 
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here


int main()
{
    //since globalId is accessible we use it directly
  const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
  const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;

  //const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly. 
  int employeeID2 = newNextID(); //employeeID3 = 0;
  int employeeID2 = newNextID(); //employeeID3 = 1;

  return 0;
}

Mais explicações sobre a escolha da variável estática global e local estática podem ser encontradas neste encadeamento stackoverflow

(ii) palavra-chave estática para variáveis ​​dentro de um bloco local sem nome.

variáveis ​​estáticas dentro de um bloco local (não um bloco funcional) não podem ser acessadas fora do bloco depois que o bloco local fica fora do escopo. Sem ressalvas a esta regra.

    //localVarDemo3.cpp 
    int main()
    {

      {
          const static int static_local_scoped_variable {99};
      }//static_local_scoped_variable goes out of scope

      //the line below causes compilation error
      //do_something is an arbitrary function
      do_something(static_local_scoped_variable);
      return 0;
    }

O C ++ 11 introduziu a palavra-chave constexprque garante a avaliação de uma expressão em tempo de compilação e permite que o compilador otimize o código. Agora, se o valor de uma variável const estática dentro de um escopo for conhecido em tempo de compilação, o código será otimizado de maneira semelhante à que existe constexpr. Aqui está um pequeno exemplo

Eu recomendo que os leitores também procurem a diferença entre constexpre static constpara variáveis neste encadeamento de fluxo de pilha . isso conclui minha explicação para a palavra-chave estática aplicada às variáveis.

B. Palavra-chave 'estática' usada para funções

em termos de funções, a palavra-chave estática tem um significado direto. Aqui, refere - se ao vínculo da função Normalmente, todas as funções declaradas em um arquivo cpp têm vínculo externo por padrão, ou seja, uma função definida em um arquivo pode ser usada em outro arquivo cpp por declaração direta.

usando uma palavra-chave estática antes da declaração da função limitar sua ligação a interna , ou seja, uma função estática não pode ser usada dentro de um arquivo fora de sua definição.

C. Staitc Palavra-chave usada para variáveis-membro e funções de classes

1. palavra-chave 'estática' para variáveis-membro de classes

Eu começo diretamente com um exemplo aqui

#include <iostream>

class DesignNumber
{
  private:

      static int m_designNum;  //design number
      int m_iteration;     // number of iterations performed for the design

  public:
    DesignNumber() {     }  //default constructor

   int  getItrNum() //get the iteration number of design
   {
      m_iteration = m_designNum++;
      return m_iteration;
   }
     static int m_anyNumber;  //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
                     // note : no need of static keyword here
                     //causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public 
                                    static member  */
enter code here

int main()
{
   DesignNumber firstDesign, secondDesign, thirdDesign;
   std::cout << firstDesign.getItrNum() << "\n";  //prints 0
   std::cout << secondDesign.getItrNum() << "\n"; //prints 1
   std::cout << thirdDesign.getItrNum() << "\n";  //prints 2

   std::cout << DesignNumber::m_anyNumber++ << "\n";  /* no object
                                        associated with m_anyNumber */
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101

   return 0;
}

Neste exemplo, a variável estática m_designNum mantém seu valor e essa variável de membro particular privada (por ser estática) é compartilhada entre todas as variáveis ​​do tipo de objeto DesignNumber

Também como outras variáveis ​​de membro, as variáveis ​​de membro estáticas de uma classe não estão associadas a nenhum objeto de classe, o que é demonstrado pela impressão de anyNumber na função principal

variáveis ​​de membro estático const vs não-const na classe

(i) variáveis ​​de membro estático da classe não const No exemplo anterior, os membros estáticos (públicos e privados) eram não constantes. O padrão ISO proíbe que os membros estáticos não-constantes sejam inicializados na classe. Portanto, como no exemplo anterior, eles devem ser inicializados após a definição da classe, com a ressalva de que a palavra-chave estática precisa ser omitida

(ii) variáveis ​​de membro const-static da classe isso é simples e segue a convenção de outra inicialização de variável de membro const, ou seja, as variáveis ​​de membro const static de uma classe podem ser inicializadas no ponto da declaração e no final da declaração da declaração de classe com uma ressalva de que a palavra-chave const precisa ser adicionada ao membro estático ao ser inicializada após a definição da classe.

No entanto, eu recomendaria inicializar as variáveis ​​de membro estático const no ponto de declaração. Isso acompanha a convenção C ++ padrão e torna o código mais limpo

Para obter mais exemplos sobre variáveis ​​estáticas de membros em uma classe, consulte o seguinte link em learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. Palavra-chave 'estática' para função de membro de classes

Assim como variáveis ​​de membro de classes podem, ser estáticas, também podem funções de membro de classes. As funções de membro normais das classes são sempre associadas a um objeto do tipo de classe. Por outro lado, funções de membro estáticas de uma classe não estão associadas a nenhum objeto da classe, ou seja, elas não possuem esse ponteiro.

Em segundo lugar, como as funções de membro estático da classe não possuem esse ponteiro, elas podem ser chamadas usando o nome da classe e o operador de resolução do escopo na função principal (ClassName :: functionName ();)

Terceiro, as funções de membro estático de uma classe só podem acessar variáveis ​​de membro estático de uma classe, pois as variáveis ​​de membro não estático de uma classe devem pertencer a um objeto de classe.

Para obter mais exemplos de funções membro estáticas em uma classe, consulte o seguinte link em learncpp.com

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/

ggulgulia
fonte
1
1) Antes do c ++ 17, somente variáveis ​​integráveis ​​estáticas de membros const podem ser inicializadas na classe, por exemplo struct Foo{static const std::string name = "cpp";};, erro, namedevem ser definidas fora da classe; com variáveis ​​embutidas introduzidas no c ++ 17, é possível codificar: struct Foo{static inline const std::string name = "cpp";};2) As funções membro / membro estáticas públicas podem ser acessadas pelo nome da classe com o operador de resolução de escopo e também uma instância com o operador de ponto (por exemplo: instance.some_static_method ())
oz1
"M_anyVariable" não deveria se tornar "m_anyNumber"? no seu último exemplo de código?
gebbissimo 27/09/19
Não posso julgar a integridade e a exatidão da resposta, mas parece realmente abrangente e fácil de seguir. Muito obrigado! Se você quiser aprimorá-lo, um breve resumo no início pode ser benéfico, pois é um texto bastante longo e os pontos principais podem ser facilmente visualizados como uma lista aninhada ou um diagrama em árvore para pessoas que conhecem termos como "interno / externo" linkage "
gebbissimo 27/09/18
18

Na verdade, é bastante simples. Se você declarar uma variável como estática no escopo de uma função, seu valor será preservado entre chamadas sucessivas a essa função. Assim:

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

será exibido em 678vez de 666, porque lembra o valor incrementado.

Quanto aos membros estáticos, eles preservam seu valor nas instâncias da classe. Portanto, o seguinte código:

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

imprimirá 4, porque first.a e second.a são essencialmente a mesma variável. Quanto à inicialização, veja esta pergunta.

Maciej Stachowski
fonte
Isso não trata das variáveis ​​de escopo do espaço para nome.
Michael Hagar
10

Quando você declara uma staticvariável no escopo do arquivo, essa variável está disponível apenas nesse arquivo específico (tecnicamente, a * unidade de tradução, mas não vamos complicar muito isso). Por exemplo:

a.cpp

static int x = 7;

void printax()
{
    cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;

void printbx()
{
    cout << "from b.cpp: x=" << x << endl;
}

main.cpp:

int main(int, char **)
{
    printax(); // Will print 7
    printbx(); // Will print 9

    return 0;
}

Para uma variável local , staticsignifica que a variável será inicializada com zero e manterá seu valor entre as chamadas:

unsigned int powersoftwo()
{
    static unsigned lastpow;

    if(lastpow == 0)
        lastpow = 1;
    else
        lastpow *= 2;

    return lastpow;
}

int main(int, char **)
{
    for(int i = 0; i != 10; i++)
        cout << "2^" << i << " = " << powersoftwo() << endl;
}

Para variáveis ​​de classe , isso significa que há apenas uma única instância dessa variável que é compartilhada entre todos os membros dessa classe. Dependendo das permissões, a variável pode ser acessada de fora da classe usando seu nome completo.

class Test
{
private:
    static char *xxx;

public:
    static int yyy;

public:
    Test()
    {        
        cout << this << "The static class variable xxx is at address "
             << static_cast<void *>(xxx) << endl;
        cout << this << "The static class variable yyy is at address "
             << static_cast<void *>(&y) << endl;
    }
};

// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;

int main(int, char **)
{
    Test t1;
    Test t2;

    Test::yyy = 666;

    Test t3;
};

Marcar uma função que não seja de classe como statictorna a função acessível apenas a partir desse arquivo e inacessível a partir de outros arquivos.

a.cpp

static void printfilename()
{ // this is the printfilename from a.cpp - 
  // it can't be accessed from any other file
    cout << "this is a.cpp" << endl;
}

b.cpp

static void printfilename()
{ // this is the printfilename from b.cpp - 
  // it can't be accessed from any other file
    cout << "this is b.cpp" << endl;
}

Para funções de membro da classe, marcá-las como staticsignifica que a função não precisa ser chamada em uma instância específica de um objeto (ou seja, não possui um thisponteiro).

class Test
{
private:
    static int count;

public:
    static int GetTestCount()
    {
        return count;
    };

    Test()
    {
        cout << this << "Created an instance of Test" << endl;
        count++;
    }

    ~Test()
    {
        cout << this << "Destroyed an instance of Test" << endl;
        count--;
    }
};

int Test::count = 0;

int main(int, char **)
{
    Test *arr[10] = { NULL };

    for(int i = 0; i != 10; i++)
        arr[i] = new Test();

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    // now, delete them all except the first and last!
    for(int i = 1; i != 9; i++)
        delete arr[i];        

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[0];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[9];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    return 0;
}
Nik Bougalis
fonte
8

Variáveis ​​estáticas são compartilhadas entre todas as instâncias de uma classe, em vez de cada classe ter sua própria variável.

class MyClass
{
    public:
    int myVar; 
    static int myStaticVar;
};

//Static member variables must be initialized. Unless you're using C++11, or it's an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;

MyClass classA;
MyClass classB;

Cada instância do 'MyClass' possui seu próprio 'myVar', mas compartilha o mesmo 'myStaticVar'. Na verdade, você nem precisa de uma instância do MyClass para acessar 'myStaticVar' e pode acessá-la fora da classe, assim:

MyClass::myStaticVar //Assuming it's publicly accessible.

Quando usada dentro de uma função como variável local (e não como variável de membro da classe), a palavra-chave estática faz algo diferente. Permite criar uma variável persistente, sem fornecer escopo global.

int myFunc()
{
   int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
   myVar++;

   //Given the above code, this will *always* print '1'.
   std::cout << myVar << std::endl;

   //The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
   static int myStaticVar = 0;

   //Each time the code reaches here, myStaticVar is incremented.
   myStaticVar++;

   //This will print a continuously incrementing number,
   //each time the function is called. '1', '2', '3', etc...
   std::cout << myStaticVar << std::endl;
}

É uma variável global em termos de persistência ... mas sem ser global em escopo / acessibilidade.

Você também pode ter funções de membro estáticas. Funções estáticas são basicamente funções não membros, mas dentro do espaço de nome do nome da classe e com acesso privado aos membros da classe.

class MyClass
{
    public:
    int Func()
    {
        //...do something...
    }

    static int StaticFunc()
    {
        //...do something...
    }
};

int main()
{
   MyClass myClassA;
   myClassA.Func(); //Calls 'Func'.
   myClassA.StaticFunc(); //Calls 'StaticFunc'.

   MyClass::StaticFunc(); //Calls 'StaticFunc'.
   MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!

   return 0;
}

Quando você chama uma função membro, há um parâmetro oculto chamado 'this', que é um ponteiro para a instância da classe que está chamando a função. As funções de membro estático não possuem esse parâmetro oculto ... elas podem ser chamadas sem uma instância de classe, mas também não podem acessar variáveis ​​de membro não estáticas de uma classe, porque não possuem um ponteiro 'this' para trabalhar. Eles não estão sendo chamados em nenhuma instância de classe específica.

Jamin Gray
fonte
1
"Supondo que seja publicamente acessível." - não é.
Luchian Grigore 5/03
2
myStaticVarprecisa ser definido também. É meio importante mencionar que, ao responder uma pergunta sobre a semântica da staticpalavra - chave, você não acha?
Pretorian
@Pretetorian: Obrigado, corrigido.
Jamin Grey
1
@JaminGrey Por "estático autônomo", eu quis dizer funções estáticas de não-membro, e as escrevo sempre que preciso de alguma nova funcionalidade apenas no arquivo CPP atual e não desejo que o vinculador tenha que processar um símbolo adicional.
VR
1
@VR Odd! Eu nunca soube que essa funcionalidade existia. Obrigado por ampliar meu conhecimento!
Jamim cinzento
1

Como não sou programador em C, não posso fornecer informações sobre o uso da estática em um programa em C, mas quando se trata de programação orientada a objetos, a estática basicamente declara uma variável, uma função ou uma classe como a mesma. durante toda a vida do programa. Considere por exemplo.

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
private:
    void somePrivateMethod();
};

Quando você instancia esta classe no seu Main, você faz algo assim.

int main()
{
   A a1;
   //do something on a1
   A a2;
   //do something on a2
}

Essas duas instâncias de classe são completamente diferentes uma da outra e operam independentemente uma da outra. Mas se você recriar a classe A assim.

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
    static int x;
private:
    void somePrivateMethod();
};

Vamos voltar para o principal novamente.

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   A a2;
   a2.x++;
   //do something on a2
}

Então a1 e a2 compartilhariam a mesma cópia de int x, pelo que qualquer operação em x em a1 influenciaria diretamente as operações de x em a2. Então, se eu fosse fazer isso

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   cout << a1.x << endl; //this would be 1
   A a2;
   a2.x++;
   cout << a2.x << endl; //this would be 2 
   //do something on a2
}

Ambas as instâncias da classe A compartilham variáveis ​​e funções estáticas. espero que isso responda sua pergunta. Meu conhecimento limitado de C me permite dizer que definir uma função ou variável como estática significa que só é visível para o arquivo que a função ou variável é definida como estática. Mas isso seria melhor respondido por um sujeito C e não por mim. O C ++ permite que as formas C e C ++ declarem suas variáveis ​​como estáticas, pois são completamente compatíveis com C.

David Tr
fonte
1

O que isso significa com variável local? Essa é uma variável local de função?

Sim - não global, como uma variável local de função.

Como há também que, quando você declara uma função local como estática, ela é inicializada apenas uma vez, na primeira vez em que entra nessa função.

Certo.

Ele também fala apenas sobre a duração do armazenamento em relação aos membros da classe, que tal não ser específico da instância, isso também é uma propriedade do static não? Ou é a duração do armazenamento?

class R { static int a; }; // << static lives for the duration of the program

isto é, todas as instâncias de Rcompartilhamento int R::a- int R::anunca são copiadas.

Agora, e o caso com escopo estático e de arquivo?

Efetivamente, um global que possui construtor / destruidor, quando apropriado - a inicialização não é adiada até o acesso.

Como estática se relaciona com a ligação de uma variável?

Para uma função local, é externa. Acesso: É acessível para a função (a menos, é claro, você a devolve).

Para uma classe, é externa. Acesso: aplicam-se especificadores de acesso padrão (público, protegido, privado).

static também pode especificar ligação interna, dependendo de onde é declarada (arquivo / espaço para nome).

Toda essa palavra-chave estática é totalmente confusa

Tem muitos propósitos em C ++.

alguém pode esclarecer os diferentes usos do inglês e também me dizer quando inicializar um aluno estático?

Ele é inicializado automaticamente antes mainse estiver carregado e tiver um construtor. Pode parecer uma coisa boa, mas a ordem de inicialização está muito além do seu controle, portanto, é muito difícil manter uma inicialização complexa, e você deseja minimizar isso - se precisar de uma estática, a função local é muito melhor nas bibliotecas e projetos. Quanto aos dados com duração de armazenamento estático, tente minimizar esse design, principalmente se for mutável (variáveis ​​globais). O 'tempo' de inicialização também varia por vários motivos - o carregador e o kernel possuem alguns truques para minimizar as pegadas de memória e adiar a inicialização, dependendo dos dados em questão.

justin
fonte
1

Objeto estático: podemos definir estáticos para os membros da classe usando a palavra-chave static. Quando declaramos um membro de uma classe como estático, significa que não importa quantos objetos da classe sejam criados, há apenas uma cópia do membro estático.

Um membro estático é compartilhado por todos os objetos da classe. Todos os dados estáticos são inicializados como zero quando o primeiro objeto é criado, se nenhuma outra inicialização estiver presente. Não podemos colocá-lo na definição de classe, mas ele pode ser inicializado fora da classe, como feito no exemplo a seguir, redeclarando a variável estática, usando o operador de resolução de escopo :: para identificar a qual classe pertence.

Vamos tentar o exemplo a seguir para entender o conceito de membros de dados estáticos:

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects.
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

Quando o código acima é compilado e executado, produz o seguinte resultado:

Constructor called.
Constructor called.
Total objects: 2

Membros da função estática: Ao declarar um membro da função como estático, você o torna independente de qualquer objeto específico da classe. Uma função de membro estática pode ser chamada mesmo que não exista nenhum objeto da classe e as funções estáticas sejam acessadas usando apenas o nome da classe e o operador de resolução do escopo ::.

Uma função de membro estático pode acessar apenas membros de dados estáticos, outras funções de membros estáticos e quaisquer outras funções de fora da classe.

As funções de membro estático têm um escopo de classe e não têm acesso ao ponteiro this da classe. Você pode usar uma função de membro estático para determinar se alguns objetos da classe foram criados ou não.

Vamos tentar o exemplo a seguir para entender o conceito de membros da função estática:

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{

   // Print total number of objects before creating object.
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

Quando o código acima é compilado e executado, produz o seguinte resultado:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
Nima Soroush
fonte
1
Seria direito de mencionar que este paradigmas foram retirados de tutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG