La parola chiave statica e i suoi vari usi in C ++


195

La parola chiave staticha molti significati in C ++ che trovo molto confusi e non riesco mai a pensare a come dovrebbe effettivamente funzionare.

Da quello che ho capito c'è la staticdurata della memorizzazione, il che significa che dura per la durata del programma nel caso di un globale, ma quando si parla di un locale, significa che è zero inizializzato per impostazione predefinita.

Lo standard C ++ dice questo per i membri dei dati di classe con la parola chiave static:

3.7.1 Durata dell'archiviazione statica [basic.stc.static]

3 La parola chiave static può essere utilizzata per dichiarare una variabile locale con durata della memoria statica.

4 La parola chiave static applicata a un membro di dati di classe in una definizione di classe fornisce la durata di memorizzazione statica del membro di dati.

Cosa significa con variabile locale ? È una variabile locale di funzione? Perché c'è anche che quando dichiari una funzione locale in staticquanto viene inizializzata una sola volta, la prima volta che entra in questa funzione.

Parla anche della durata della memoria per quanto riguarda i membri della classe, che dire che non è specifico dell'istanza, che è anche una proprietà di staticno? O è quella durata della conservazione?

E che dire del caso con statice l'ambito del file? Per impostazione predefinita, tutte le variabili globali hanno una durata di archiviazione statica? Quanto segue (dalla sezione 3.7.1) sembra indicarlo:

1 Tutte le variabili che non hanno una durata della memoria dinamica, non hanno una durata della memoria thread e non sono locali hanno una durata della memoria statica. L'archiviazione per queste entità deve durare per la durata del programma (3.6.2, 3.6.3)

Come si staticcollega al collegamento di una variabile?

Tutta questa staticparola chiave è decisamente confusa, qualcuno può chiarire i diversi usi dell'inglese e dirmi anche quando inizializzare un staticmembro della classe?


Risposte:


147

variabili:

staticesistono variabili per la "durata" dell'unità di traduzione in cui è definita , e:

  • Se si trova in un ambito dello spazio dei nomi (ovvero al di fuori di funzioni e classi), non è possibile accedervi da nessun'altra unità di traduzione. Questo è noto come "collegamento interno" o "durata della memoria statica". (Non farlo nelle intestazioni tranne constexpr. Nient'altro, e finisci con una variabile separata in ogni unità di traduzione, il che è pazzesco e confuso)
  • Se è una variabile in una funzione , non è possibile accedervi dall'esterno della funzione, proprio come qualsiasi altra variabile locale. (questo è il locale che hanno menzionato)
  • i membri della classe non hanno un ambito limitato a causa di static, ma possono essere indirizzati dalla classe così come da un'istanza (come std::string::npos). [Nota: è possibile dichiarare membri statici in una classe, ma in genere devono comunque essere definiti in un'unità di traduzione (file cpp) e, come tale, ce n'è solo uno per classe]

posizioni come codice:

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

Prima di eseguire qualsiasi funzione in un'unità di traduzione (possibilmente dopo l' mainesecuzione iniziata), le variabili con durata di memorizzazione statica (ambito dello spazio dei nomi) in tale unità di traduzione saranno "inizializzate costantemente" ( constexprdove possibile, o altrimenti zero), e quindi non- i locali vengono "inizializzati dinamicamente" correttamente nell'ordine in cui sono definiti nell'unità di traduzione (per cose del genere std::string="HI";non lo sono constexpr). Infine, la statica locale della funzione verrà inizializzata la prima volta che l'esecuzione "raggiunge" la linea in cui sono dichiarate. Tutte le staticvariabili sono state tutte distrutte nell'ordine inverso all'inizializzazione.

Il modo più semplice per ottenere tutto questo è fare in modo che tutte le variabili statiche che non siano constexprinizializzate in locali statici funzionino, il che assicura che tutte le statistiche / i globali siano inizializzati correttamente quando si tenta di utilizzarli, indipendentemente dall'operazione, impedendo così l' inizializzazione statica ordina il fiasco .

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

Fai attenzione, perché quando la specifica dice che le variabili di ambito namespace hanno "durata della memoria statica" per impostazione predefinita, significano il bit "durata dell'unità di traduzione", ma ciò non significa che non sia possibile accedervi al di fuori del file.

funzioni

Significativamente più semplice, staticviene spesso utilizzato come funzione di membro di classe e solo molto raramente per una funzione indipendente.

Una funzione membro statica differisce da una normale funzione membro in quanto può essere chiamata senza un'istanza di una classe e poiché non ha istanza, non può accedere a membri non statici della classe. Le variabili statiche sono utili quando si desidera avere una funzione per una classe che non fa assolutamente riferimento a nessun membro dell'istanza o per la gestione delle staticvariabili dei membri.

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
}

Una staticfunzione libera significa che la funzione non verrà citata da nessun'altra unità di traduzione, e quindi il linker può ignorarla completamente. Questo ha un piccolo numero di scopi:

  • Può essere utilizzato in un file cpp per garantire che la funzione non venga mai utilizzata da nessun altro file.
  • Può essere inserito in un'intestazione e ogni file avrà la propria copia della funzione. Non utile, poiché inline fa praticamente la stessa cosa.
  • Accelera i tempi di collegamento riducendo il lavoro
  • Possono mettere una funzione con lo stesso nome in ogni unità di traduzione e possono fare tutte cose diverse. Ad esempio, è possibile inserire un static void log(const char*) {}file in ciascun file cpp e tutti possono accedere in modo diverso.

1
E i membri della classe? Non è un terzo caso separato?
Étienne

4
@Etienne: i membri dei dati della classe statica sono gli stessi delle variabili globali statiche, tranne per il fatto che è possibile accedervi da altre unità di traduzione e qualsiasi accesso (ad eccezione delle funzioni membro) deve specificare l' classname::ambito. Le funzioni statiche dei membri della classe sono come le funzioni globali ma sono mirate alla classe, o come i membri normali ma senza this(non è una scelta - quei due dovrebbero essere equivalenti).
Steve314

1
@LuchianGrigore: mentre vedo il tuo punto, non sono sicuro di quale formulazione usare.
Mooing Duck

1
@ Steve314: capisco cosa intendi, ma quando si tratta di un termine così orribilmente sovraccarico come statico , vorrei che fossimo tutti un po 'più attenti. In particolare tutte le variabili globali (a livello di spazio dei nomi) hanno una durata statica, quindi si può intendere l' aggiunta di variabili statiche globali statichenamespace A { static int x; } , il che significa collegamento interno ed è molto diverso dal comportamento dei membri di dati della classe statica .
David Rodríguez - dribeas,

1
"Se si trova in un ambito di spazio dei nomi, non è possibile accedervi da nessun'altra unità di traduzione ..." Cosa intendi se si trova in un ambito di spazio dei nomi? Non è sempre così, potresti dare un esempio e un contro esempio?
AturSams,

66

La durata della memorizzazione statica significa che la variabile risiede nello stesso posto in memoria per tutta la durata del programma.

Il collegamento è ortogonale a questo.

Penso che questa sia la distinzione più importante che puoi fare. Capire questo e il resto, oltre a ricordarlo, dovrebbe venire facile (non rivolgendosi direttamente a @Tony, ma chiunque potrebbe leggerlo in futuro).

La parola chiave staticpuò essere utilizzata per indicare il collegamento interno e l' archiviazione statica, ma in sostanza sono diversi.

Cosa significa con variabile locale? È una variabile locale di funzione?

Sì. Indipendentemente da quando la variabile viene inizializzata (alla prima chiamata alla funzione e quando il percorso di esecuzione raggiunge il punto di dichiarazione), risiederà nello stesso posto in memoria per la durata del programma. In questo caso, staticfornisce memoria statica.

E che dire del caso con ambito statico e file? Per impostazione predefinita, tutte le variabili globali hanno una durata di archiviazione statica?

Sì, tutti i globi hanno per definizione una durata di archiviazione statica (ora che abbiamo chiarito cosa significa). Ma le variabili con ambito namespace non sono dichiarate con static, perché ciò darebbe loro un collegamento interno, quindi una variabile per unità di traduzione.

In che modo statico si collega al collegamento di una variabile?

Fornisce un collegamento interno per le variabili con ambito dei namespace. Fornisce ai membri e alle variabili locali una durata di archiviazione statica.

Espandiamo su tutto questo:

//

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
}

Tutta questa parola chiave statica è decisamente confusa

Sicuramente, a meno che tu non ne abbia familiarità. :) Cercando di evitare di aggiungere nuove parole chiave alla lingua, il comitato ha riutilizzato questa, IMO, a tal fine - confusione. È usato per indicare cose diverse (potrei dire, probabilmente cose opposte).


1
Fammi capire bene: lo dici quando lo dico static int xnell'ambito dello spazio dei nomi, questo gli dà memoria non statica ?
Michael Hagar,

30

Per chiarire la domanda, preferirei categorizzare l'uso della parola chiave "statica" in tre forme diverse:

(UN). variabili

(B). funzioni

(C). variabili / funzioni dei membri delle classi

la spiegazione seguente segue per ciascuna delle sottorubriche:

(A) parola chiave "statica" per variabili

Questo può essere un po 'complicato, tuttavia, se spiegato e compreso correttamente, è piuttosto semplice.

Per spiegarlo, in primo luogo è davvero utile conoscere l' ambito, la durata e il legame delle variabili, senza le quali le cose sono sempre difficili da vedere attraverso il concetto oscuro di parola chiave staic

1. Ambito : determina dove è accessibile la variabile nel file. Può essere di due tipi: (i) Ambito locale o di blocco . (ii) Ambito globale

2. Durata : determina quando una variabile viene creata e distrutta. Anche in questo caso è di due tipi: (i) Durata di memorizzazione automatica (per variabili con ambito locale o di blocco). (ii) Durata dell'archiviazione statica (per variabili con ambito globale o variabili locali (in una funzione o in un blocco di codice) con identificatore statico ).

3. Collegamento : determina se è possibile accedere (o collegare) a una variabile in un altro file. Anche in questo caso (e per fortuna) è di due tipi: (i) Linkage interno (per variabili che hanno Block Scope e Global Scope / File Scope / Global Namespace scope) (ii) Link esterno (per variabili che hanno solo Global Scope / File Scope / Ambito globale dello spazio dei nomi)

Facciamo riferimento di seguito un esempio per una migliore comprensione delle semplici variabili globali e locali (nessuna variabile locale con durata dell'archiviazione statica):

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

Ora arriva il concetto di collegamento. Quando una variabile globale definita in un file deve essere utilizzata in un altro file, il collegamento della variabile svolge un ruolo importante.

Il collegamento di variabili globali è specificato dalle parole chiave: (i) statico e, (ii) esterno

(Ora ottieni la spiegazione)

la parola chiave static può essere applicata a variabili con ambito locale e globale e in entrambi i casi significano cose diverse. Spiegherò innanzitutto l'uso della parola chiave "statica" nelle variabili con portata globale (dove chiarirò anche l'uso della parola chiave "extern") e successivamente le parole chiave con portata locale.

1. Parola chiave statica per variabili con portata globale

Le variabili globali hanno una durata statica, il che significa che non escono dall'ambito di applicazione quando termina un determinato blocco di codice (ad esempio main ()) in cui viene utilizzato. A seconda del collegamento, è possibile accedervi solo all'interno dello stesso file in cui sono dichiarati (per variabile globale statica) o all'esterno del file anche al di fuori del file in cui sono dichiarati (variabili globali di tipo extern)

Nel caso di una variabile globale con specificatore extern e se si accede a questa variabile all'esterno del file in cui è stata inizializzata, deve essere dichiarata in avanti nel file in cui viene utilizzata, proprio come una funzione deve essere inoltrata dichiarato se la sua definizione si trova in un file diverso da quello in cui viene utilizzato.

Al contrario, se la variabile globale ha una parola chiave statica, non può essere utilizzata in un file al di fuori del quale è stata dichiarata.

(vedi esempio sotto per chiarimenti)

per esempio:

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

ora qualsiasi variabile in c ++ può essere una const o una non const e per ogni 'const-ness' otteniamo due casi di collegamento c ++ predefinito, nel caso in cui nessuno sia specificato:

(i) Se una variabile globale non è const, il suo collegamento è esterno per impostazione predefinita , ovvero è possibile accedere alla variabile globale non const in un altro file .cpp mediante dichiarazione in avanti utilizzando la parola chiave extern (in altre parole, non const global le variabili hanno un collegamento esterno (con durata statica ovviamente)). Anche l'uso della parola chiave extern nel file originale in cui è stata definita è ridondante. In questo caso per rendere inaccessibile una variabile globale non const al file esterno, utilizzare l'identificatore "statico" prima del tipo di variabile .

(ii) Se una variabile globale è const, il suo collegamento è statico per impostazione predefinita , ovvero non è possibile accedere a una variabile globale const in un file diverso da dove è definita, (in altre parole, le variabili globali const hanno un collegamento interno (con durata statica ovviamente)). Anche l'uso di parole chiave statiche per impedire l'accesso a una variabile globale const in un altro file è ridondante. Qui, per fare in modo che una variabile globale const abbia un collegamento esterno, utilizzare l'identificatore 'extern' prima del tipo di variabile

Ecco un riepilogo per le variabili di ambito globali con vari collegamenti

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

Successivamente esamineremo come si comportano le variabili globali di cui sopra quando vi si accede in un altro file.

//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. Parola chiave statica per variabili con ambito locale

Aggiornamenti (agosto 2019) su parola chiave statica per variabili nell'ambito locale

Questo ulteriore può essere suddiviso in due categorie:

(i) parola chiave statica per variabili all'interno di un blocco funzione e (ii) parola chiave statica per variabili all'interno di un blocco locale senza nome.

(i) parola chiave statica per variabili all'interno di un blocco funzione.

In precedenza, ho detto che le variabili con ambito locale hanno una durata automatica, cioè diventano esistenti quando si inserisce il blocco (sia esso un blocco normale, sia esso un blocco funzione) e cessano di esistere quando il blocco termina, per farla breve, variabili con ambito locale hanno durata automatica e le variabili di durata automatica (e gli oggetti) non hanno alcun collegamento, il che significa che non sono visibili al di fuori del blocco di codice.

Se l' identificatore statico viene applicato a una variabile locale all'interno di un blocco funzione, cambia la durata della variabile da automatica a statica e la sua durata è l'intera durata del programma, il che significa che ha una posizione di memoria fissa e il suo valore viene inizializzato solo una volta prima dell'avvio del programma, come indicato nel riferimento cpp (l'inizializzazione non deve essere confusa con l'assegnazione)

diamo un'occhiata a un esempio.

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

Guardando il criterio sopra per variabili locali statiche e variabili globali statiche, si potrebbe essere tentati di chiedere, quale potrebbe essere la differenza tra loro. Mentre le variabili globali sono accessibili in qualsiasi punto all'interno del codice (a stesso così come unità di traduzione differenti a seconda del const -ness e extern -ness), una variabile statica definita all'interno di un blocco funzione non è direttamente accessibile. La variabile deve essere restituita dal valore della funzione o dal riferimento. Dimostriamolo con un esempio:

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

Ulteriori spiegazioni sulla scelta della variabile statica globale e statica locale possono essere trovate su questo thread stackoverflow

(ii) parola chiave statica per variabili all'interno di un blocco locale senza nome.

Le variabili statiche all'interno di un blocco locale (non un blocco funzione) non sono accessibili al di fuori del blocco una volta che il blocco locale esce dall'ambito. Nessun avvertimento per questa regola.

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

C ++ 11 ha introdotto la parola chiave constexprche garantisce la valutazione di un'espressione in fase di compilazione e consente al compilatore di ottimizzare il codice. Ora se il valore di una variabile const statica all'interno di un ambito è noto al momento della compilazione, il codice è ottimizzato in modo simile a quello con constexpr. Ecco un piccolo esempio

Consiglio anche ai lettori di cercare la differenza tra constexpre static constper le variabili in questo thread di StackOverflow . questo conclude la mia spiegazione per la parola chiave statica applicata alle variabili.

B. Parola chiave "statica" utilizzata per le funzioni

in termini di funzioni, la parola chiave statica ha un significato semplice. Qui, si riferisce al collegamento della funzione Normalmente tutte le funzioni dichiarate all'interno di un file cpp hanno un collegamento esterno per impostazione predefinita, vale a dire una funzione definita in un file può essere utilizzata in un altro file cpp mediante dichiarazione diretta.

l'uso di una parola chiave statica prima della dichiarazione della funzione limita il suo collegamento a interno , ovvero una funzione statica non può essere utilizzata all'interno di un file al di fuori della sua definizione.

C. Parola chiave Staitc utilizzata per le variabili membro e le funzioni delle classi

1. Parola chiave "statica" per le variabili membro delle classi

Comincio direttamente con un esempio qui

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

In questo esempio, la variabile statica m_designNum conserva il suo valore e questa singola variabile del membro privato (perché è statica) è condivisa in b / n tutte le variabili del tipo di oggetto DesignNumber

Analogamente ad altre variabili membro, le variabili membro statiche di una classe non sono associate ad alcun oggetto di classe, come dimostrato dalla stampa di anyNumber nella funzione principale

const vs variabili non costanti del membro statico nella classe

(i) variabili membro statiche non costanti Nell'esempio precedente i membri statici (sia pubblici che privati) erano non costanti. La norma ISO vieta l'inizializzazione di membri statici non costanti nella classe. Quindi, come nell'esempio precedente, devono essere inizializzati dopo la definizione della classe, con l'avvertenza che la parola chiave statica deve essere omessa

(ii) variabili membro const-static della classe questo è semplice e si accompagna alla convenzione di inizializzazione di altre variabili const const, cioè le variabili costanti di un membro const di una classe possono essere inizializzate al punto di dichiarazione e possono essere inizializzate alla fine della dichiarazione di classe con un avvertimento che la parola chiave const deve essere aggiunta al membro statico quando viene inizializzata dopo la definizione della classe.

Vorrei tuttavia raccomandare di inizializzare le variabili costanti dei membri statici nel punto di dichiarazione. Questo vale con la convenzione C ++ standard e rende il codice più pulito

per ulteriori esempi sulle variabili statiche dei membri in una classe, cercare il seguente collegamento da learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. Parola chiave "statica" per la funzione membro delle classi

Proprio come le variabili membro delle classi possono essere statiche, così come le funzioni membro delle classi. Le normali funzioni membro delle classi sono sempre associate a un oggetto del tipo di classe. Al contrario, le funzioni membro statiche di una classe non sono associate a nessun oggetto della classe, cioè non hanno * questo puntatore.

In secondo luogo, poiché le funzioni membro statiche della classe non hanno * questo puntatore, possono essere chiamate usando il nome della classe e l'operatore di risoluzione dell'ambito nella funzione principale (ClassName :: functionName ();)

Le funzioni di membro terzo statico di una classe possono accedere solo alle variabili membro statico di una classe, poiché le variabili membro non statico di una classe devono appartenere a un oggetto classe.

per ulteriori esempi sulle funzioni dei membri statici in una classe, cercare il seguente collegamento da learncpp.com

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


1
1) Prima di c ++ 17 è possibile inizializzare in classe solo le variabili membro costanti statiche integrali, ad esempio struct Foo{static const std::string name = "cpp";};errore, che namedevono essere definite al di fuori della classe; con variabili inline integrate in c ++ 17 si può codificare: struct Foo{static inline const std::string name = "cpp";};2) È possibile accedere alle funzioni di membro / membro statico pubblico tramite il nome della classe con l'operatore di risoluzione dell'ambito e anche un'istanza con l'operatore punto (ad esempio: instance.some_static_method ())
oz1

"M_anyVariable" non dovrebbe diventare "m_anyNumber"? nel tuo ultimo esempio di codice?
gebbissimo,

Non posso giudicare la completezza e la correttezza della risposta, ma sembra davvero completa ed è stato facile seguirlo. Molte grazie! Se si desidera migliorarlo, un breve riassunto all'inizio potrebbe essere utile poiché è un testo piuttosto lungo e i punti principali potrebbero essere facilmente visualizzati come un elenco nidificato o un diagramma ad albero per persone che conoscono termini come "interno / esterno linkage "
gebbissimo,

18

In realtà è abbastanza semplice. Se si dichiara una variabile come statica nell'ambito di una funzione, il suo valore viene conservato tra le chiamate successive a quella funzione. Così:

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

mostrerà 678invece di 666, perché ricorda il valore incrementato.

Per quanto riguarda i membri statici, mantengono il loro valore tra le istanze della classe. Quindi il seguente codice:

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

stamperà 4, perché first.a e second.a sono essenzialmente la stessa variabile. Per quanto riguarda l'inizializzazione, vedere questa domanda.


Questo non riguarda le variabili dell'ambito dello spazio dei nomi.
Michael Hagar,

10

Quando dichiari una staticvariabile nell'ambito del file, quella variabile è disponibile solo in quel particolare file (tecnicamente, l'unità di traduzione *, ma non compliciamo troppo). Per esempio:

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

Per una variabile locale , staticsignifica che la variabile verrà inizializzata a zero e manterrà il suo valore tra le chiamate:

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

Per le variabili di classe , significa che esiste una sola istanza di quella variabile condivisa tra tutti i membri di quella classe. A seconda delle autorizzazioni, è possibile accedere alla variabile dall'esterno della classe utilizzando il 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;
};

Contrassegnare una funzione non di classe in quanto staticla funzione è accessibile solo da quel file e inaccessibile da altri file.

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

Per le funzioni dei membri della classe, contrassegnarle come staticsignifica che la funzione non deve essere chiamata su una particolare istanza di un oggetto (cioè non ha un thispuntatore).

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

8

Le variabili statiche sono condivise tra ogni istanza di una classe, invece che ogni classe abbia la propria variabile.

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;

Ogni istanza di "MyClass" ha il proprio "myVar", ma condivide lo stesso "myStaticVar". In effetti, non hai nemmeno bisogno di un'istanza di MyClass per accedere a "myStaticVar" e puoi accedervi al di fuori della classe in questo modo:

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

Se usata all'interno di una funzione come variabile locale (e non come variabile membro di classe) la parola chiave statica fa qualcosa di diverso. Ti permette di creare una variabile persistente, senza dare portata globale.

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

È una variabile globale in termini di persistenza ... ma senza essere globale in ambito / accessibilità.

Puoi anche avere funzioni membro statiche. Le funzioni statiche sono sostanzialmente funzioni non membro, ma all'interno dello spazio dei nomi del nome della classe e con accesso privato ai membri della 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 chiami una funzione membro, c'è un parametro nascosto chiamato 'this', che è un puntatore all'istanza della classe che chiama la funzione. Le funzioni membro statiche non hanno quel parametro nascosto ... sono richiamabili senza un'istanza di classe, ma non possono accedere a variabili membro non statiche di una classe, perché non hanno un puntatore "this" con cui lavorare. Non vengono chiamati in nessuna istanza di classe specifica.


1
"Supponendo che sia accessibile pubblicamente." - non è.
Luchian Grigore

2
myStaticVardeve essere definito anche. È importante ricordare che quando si risponde a una domanda sulla semantica della staticparola chiave, non credi?
Pretorio

@Praetorian: Grazie, risolto.
Jamin Grey

1
@JaminGrey Con "standalone statico" intendevo funzioni statiche non membro e scrivo tali ogni volta che ho bisogno di alcune nuove funzionalità solo nel file CPP corrente e non desidero che il linker debba elaborare un simbolo aggiuntivo.
VR,

1
@VR Odd! Non ho mai saputo che esistesse la funzionalità . Grazie per aver ampliato le mie conoscenze!
Jamin Grey,

1

Non sono un programmatore C, quindi non posso darti informazioni sugli usi di static in un programma C in modo corretto, ma quando si tratta di programmazione orientata agli oggetti static dichiara sostanzialmente una variabile, o una funzione o una classe come uguali per tutta la durata del programma. Prendi ad esempio.

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

Quando crei un'istanza di questa classe nel tuo Main, fai qualcosa del genere.

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

Queste due istanze di classe sono completamente diverse l'una dall'altra e operano indipendentemente l'una dall'altra. Ma se dovessi ricreare la classe A in questo modo.

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

Torniamo di nuovo al principale.

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

Quindi a1 e a2 condividerebbero la stessa copia di int x per cui qualsiasi operazione su x in a1 influenzerebbe direttamente le operazioni di x in a2. Quindi se dovessi farlo

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
}

Entrambe le istanze della classe A condividono variabili e funzioni statiche. Spero che questo risponda alla tua domanda. La mia limitata conoscenza di C mi permette di dire che definire una funzione o una variabile come statica significa che è visibile solo al file che la funzione o la variabile è definita come statica in. Ma questo sarebbe meglio rispondere da un ragazzo C e non da me. Il C ++ consente sia al C che al C ++ di dichiarare le variabili come statiche perché è completamente retrocompatibile con C.


1

Cosa significa con variabile locale? È una variabile locale di funzione?

Sì: non globale, come una variabile locale di funzione.

Perché c'è anche che quando dichiari una funzione locale come statica che viene inizializzata solo una volta, la prima volta che entra in questa funzione.

Destra.

Parla anche della durata della memoria per quanto riguarda i membri della classe, che dire che non è specifico dell'istanza, che è anche una proprietà di no statico? O è quella durata della conservazione?

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

vale a dire, tutte le istanze di Rcondivisione int R::a- int R::anon vengono mai copiate.

E che dire del caso con ambito statico e file?

In effetti un globale che ha costruttore / distruttore ove appropriato - l'inizializzazione non viene differita fino all'accesso.

In che modo statico si collega al collegamento di una variabile?

Per una funzione locale, è esterna. Accesso: è accessibile alla funzione (a meno che, ovviamente, non venga restituita).

Per una classe, è esterna. Accesso: si applicano gli identificatori di accesso standard (pubblico, protetto, privato).

static può anche specificare il collegamento interno, a seconda di dove viene dichiarato (file / namespace).

Tutta questa parola chiave statica è decisamente confusa

Ha troppi scopi in C ++.

qualcuno può chiarire i diversi usi per l'inglese e anche dirmi quando inizializzare un membro di classe statico?

Viene inizializzato automaticamente prima mainse è caricato e ha un costruttore. Potrebbe sembrare una buona cosa, ma l'ordine di inizializzazione è in gran parte al di fuori del tuo controllo, quindi l'inizializzazione complessa diventa molto difficile da mantenere e vuoi minimizzarla - se devi avere una statica, allora funzioni le bilance locali molto meglio tra le librerie e progetti. Per quanto riguarda i dati con durata dell'archiviazione statica, dovresti cercare di ridurre al minimo questo progetto, in particolare se modificabile (variabili globali). Il "tempo" di inizializzazione varia anche per una serie di motivi: il caricatore e il kernel hanno alcuni trucchi per ridurre al minimo le impronte di memoria e rinviare l'inizializzazione, a seconda dei dati in questione.


1

Oggetto statico: possiamo definire i membri della classe statici usando una parola chiave statica. Quando dichiariamo un membro di una classe come statico significa che non importa quanti oggetti della classe vengono creati, esiste una sola copia del membro statico.

Un membro statico è condiviso da tutti gli oggetti della classe. Tutti i dati statici vengono inizializzati su zero quando viene creato il primo oggetto, se non è presente alcuna altra inizializzazione. Non possiamo inserirlo nella definizione della classe, ma può essere inizializzato al di fuori della classe, come nell'esempio seguente, dichiarando nuovamente la variabile statica, usando l'operatore di risoluzione dell'ambito :: per identificare a quale classe appartiene.

Proviamo il seguente esempio per comprendere il concetto di membri di dati statici:

#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 il codice sopra è compilato ed eseguito, produce il seguente risultato:

Constructor called.
Constructor called.
Total objects: 2

Membri di funzioni statiche: dichiarando un membro di funzione come statico, lo rendi indipendente da qualsiasi oggetto particolare della classe. Una funzione membro statica può essere chiamata anche se non esistono oggetti della classe e si accede alle funzioni statiche utilizzando solo il nome della classe e l'operatore di risoluzione dell'ambito ::.

Una funzione membro statica può accedere solo a un membro dati statico, altre funzioni membro statico e qualsiasi altra funzione esterna alla classe.

Le funzioni membro statiche hanno un ambito di classe e non hanno accesso a questo puntatore della classe. È possibile utilizzare una funzione membro statica per determinare se alcuni oggetti della classe sono stati creati o meno.

Proviamo il seguente esempio per comprendere il concetto di membri di funzioni statiche:

#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 il codice sopra è compilato ed eseguito, produce il seguente risultato:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

1
Sarebbe giusto dire che questi paradigmi sono stati presi da tutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.