Das statische Schlüsselwort und seine verschiedenen Verwendungen in C ++

195

Das Schlüsselwort statichat in C ++ mehrere Bedeutungen, die ich sehr verwirrend finde, und ich kann mich nie darüber Gedanken machen, wie es eigentlich funktionieren soll.

Soweit ich staticweiß , gibt es eine Speicherdauer, was bedeutet, dass sie im Falle eines globalen Programms für die gesamte Lebensdauer des Programms gültig ist. Wenn Sie jedoch von einem lokalen Programm sprechen, bedeutet dies, dass es standardmäßig auf Null initialisiert ist.

Der C ++ - Standard sagt dies für Klassendatenelemente mit dem Schlüsselwort static:

3.7.1 Statische Speicherdauer [basic.stc.static]

3 Mit dem Schlüsselwort static kann eine lokale Variable mit statischer Speicherdauer deklariert werden.

4 Das Schlüsselwort static, das in einer Klassendefinition auf ein Klassendatenelement angewendet wird, gibt die statische Speicherdauer des Datenelements an.

Was bedeutet das mit lokaler Variable ? Ist das eine lokale Funktionsvariable? Denn wenn Sie eine lokale Funktion deklarieren static, wird sie nur einmal initialisiert, wenn sie diese Funktion zum ersten Mal aufruft.

Es wird auch nur über die Speicherdauer in Bezug auf Klassenmitglieder gesprochen. Was ist damit, dass es nicht instanzspezifisch ist, das ist auch eine Eigenschaft von staticno? Oder ist das Lagerdauer?

Was ist nun mit dem Fall mit staticund dem Dateibereich? Werden alle globalen Variablen standardmäßig als statisch gespeichert? Folgendes (aus Abschnitt 3.7.1) scheint dies anzuzeigen:

1 Alle Variablen, die keine dynamische Speicherdauer haben, keine Thread-Speicherdauer haben und nicht lokal sind, haben eine statische Speicherdauer. Die Speicherung für diese Unternehmen dauert für die Dauer des Programms (3.6.2, 3.6.3).

In welcher staticBeziehung steht die Verknüpfung einer Variablen?

Dieses ganze staticSchlüsselwort ist geradezu verwirrend. Kann jemand die verschiedenen Verwendungszwecke für Englisch klären und mir auch sagen, wann ich ein staticKlassenmitglied initialisieren soll ?

Tony der Löwe
quelle
Verwandte: stackoverflow.com/questions/572547/what-does-static-mean-in-c
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

147

Variablen:

staticVariablen existieren für die "Lebensdauer" der Übersetzungseinheit, in der sie definiert ist , und:

  • Wenn es sich in einem Namespace-Bereich befindet (dh außerhalb von Funktionen und Klassen), kann von keiner anderen Übersetzungseinheit darauf zugegriffen werden. Dies wird als "interne Verknüpfung" oder "statische Speicherdauer" bezeichnet. (Tun Sie dies nicht in Überschriften außer constexpr. Alles andere, und Sie erhalten eine separate Variable in jeder Übersetzungseinheit, was verrückt verwirrend ist.)
  • Wenn es sich um eine Variable in einer Funktion handelt , kann nicht wie bei jeder anderen lokalen Variablen von außerhalb der Funktion darauf zugegriffen werden. (Dies ist der Ort, den sie erwähnt haben)
  • Klassenmitglieder haben keinen eingeschränkten Umfang aufgrund von static, können aber sowohl von der Klasse als auch von einer Instanz (wie std::string::npos) angesprochen werden . [Hinweis: Sie können statische Elemente in einer Klasse deklarieren , diese sollten jedoch normalerweise noch in einer Übersetzungseinheit (CPP-Datei) definiert werden. Daher gibt es nur eines pro Klasse.]

Standorte als Code:

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

Bevor eine Funktion in einer Übersetzungseinheit ausgeführt wird (möglicherweise nach Beginn der mainAusführung), werden die Variablen mit statischer Speicherdauer (Namespace-Bereich) in dieser Übersetzungseinheit "konstant initialisiert" (soweit constexprmöglich oder ansonsten auf Null) und dann nicht Einheimische werden in der Reihenfolge, in der sie in der Übersetzungseinheit definiert sind, ordnungsgemäß "dynamisch initialisiert" (für solche Dinge std::string="HI";nicht constexpr). Schließlich wird die funktionslokale Statik initialisiert, wenn die Ausführung zum ersten Mal die Zeile "erreicht", in der sie deklariert ist. Alle staticVariablen werden in umgekehrter Reihenfolge der Initialisierung zerstört.

Der einfachste Weg, dies zu constexprkorrigieren, besteht darin, alle statischen Variablen, die nicht initialisiert wurden, in statische Funktionslokale zu verwandeln. Dadurch wird sichergestellt, dass alle Ihre Statics / Globals ordnungsgemäß initialisiert werden, wenn Sie versuchen, sie unabhängig davon zu verwenden, wodurch die statische Initialisierung verhindert wird Fiasko bestellen .

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

Seien Sie vorsichtig, denn wenn die Spezifikation besagt, dass Namespace-Scope-Variablen standardmäßig eine "statische Speicherdauer" haben, bedeuten sie die "Lebensdauer der Übersetzungseinheit", aber das bedeutet nicht , dass außerhalb der Datei nicht darauf zugegriffen werden kann.

Funktionen

Deutlich einfacher, staticwird häufig als Klassenmitgliedsfunktion verwendet und nur sehr selten für eine freistehende Funktion.

Eine statische Elementfunktion unterscheidet sich von einer regulären Elementfunktion dadurch, dass sie ohne Instanz einer Klasse aufgerufen werden kann. Da sie keine Instanz hat, kann sie nicht auf nicht statische Elemente der Klasse zugreifen. Statische Variablen sind nützlich, wenn Sie eine Funktion für eine Klasse haben möchten, die sich definitiv nicht auf Instanzmitglieder bezieht, oder um staticMitgliedsvariablen zu verwalten.

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
}

Eine staticfreie Funktion bedeutet, dass die Funktion von keiner anderen Übersetzungseinheit referenziert wird und der Linker sie daher vollständig ignorieren kann. Dies hat eine kleine Anzahl von Zwecken:

  • Kann in einer CPP-Datei verwendet werden, um sicherzustellen, dass die Funktion niemals aus einer anderen Datei verwendet wird.
  • Kann in einen Header eingefügt werden und jede Datei hat eine eigene Kopie der Funktion. Nicht nützlich, da Inline so ziemlich das Gleiche tut.
  • Beschleunigt die Verbindungszeit durch Reduzierung der Arbeit
  • Kann in jede Übersetzungseinheit eine Funktion mit demselben Namen einfügen, und alle können unterschiedliche Aufgaben ausführen. Zum Beispiel könnten Sie eine static void log(const char*) {}in jede CPP-Datei einfügen, und sie könnten sich alle auf unterschiedliche Weise protokollieren.
Mooing Duck
quelle
1
Was ist mit den Teilnehmern? Ist es nicht ein dritter Einzelfall?
Étienne
4
@Etienne - Statische Klassendatenelemente sind mit statischen globalen Variablen identisch, außer dass Sie von anderen Übersetzungseinheiten aus auf sie zugreifen können und jeder Zugriff (außer von Elementfunktionen) den classname::Bereich angeben muss . Statische Klassenmitgliedsfunktionen sind wie globale Funktionen, jedoch auf die Klasse beschränkt oder wie normale Mitglieder, jedoch ohne this(das ist keine Wahl - diese beiden sollten gleichwertig sein).
Steve314
1
@LuchianGrigore: Obwohl ich Ihren Standpunkt sehe, bin ich mir nicht sicher, welchen Wortlaut ich verwenden soll.
Mooing Duck
1
@ Steve314: Ich verstehe, was du meinst, aber wenn ich mich mit einem so schrecklich überladenen Begriff wie statisch befasse , wünschte ich, wir wären alle etwas vorsichtiger. Insbesondere haben alle globalen Variablen (wirklich Namespace-Ebene) eine statische Dauer, so dass das Hinzufügen von statischen in statischen globalen Variablen als verstanden werden kann namespace A { static int x; }, was eine interne Verknüpfung bedeutet und sich stark vom Verhalten statischer Klassendatenelemente unterscheidet .
David Rodríguez - Dribeas
1
"Wenn es sich in einem Namespace-Bereich befindet, kann von keiner anderen Übersetzungseinheit darauf zugegriffen werden ..." Was meinen Sie, wenn es sich in einem Namespace-Bereich befindet? Ist das nicht immer der Fall, könnten Sie ein Beispiel und ein Gegenbeispiel geben?
AturSams
66

Statische Speicherdauer bedeutet, dass sich die Variable während der gesamten Lebensdauer des Programms an derselben Stelle im Speicher befindet.

Die Verknüpfung ist dazu orthogonal.

Ich denke, das ist der wichtigste Unterschied, den Sie machen können. Verstehen Sie dies und den Rest, und denken Sie daran, es sollte einfach sein (nicht direkt an @Tony zu sprechen, sondern wer auch immer dies in Zukunft lesen könnte).

Das Schlüsselwort statickann verwendet werden, um interne Verknüpfung und statischen Speicher zu bezeichnen, aber im Wesentlichen sind diese unterschiedlich.

Was bedeutet das mit lokaler Variable? Ist das eine lokale Funktionsvariable?

Ja. Unabhängig davon, wann die Variable initialisiert wird (beim ersten Aufruf der Funktion und wenn der Ausführungspfad den Deklarationspunkt erreicht), befindet sie sich während der gesamten Laufzeit des Programms an derselben Stelle im Speicher. In diesem Fall staticgibt es statischen Speicher.

Was ist nun mit dem Fall mit statischem und Dateibereich? Werden alle globalen Variablen standardmäßig als statisch gespeichert?

Ja, alle Globals haben per Definition eine statische Speicherdauer (jetzt, da wir geklärt haben, was das bedeutet). Aber Namespace scoped Variablen nicht mit erklärt static, weil das ihnen geben würde interne Bindung, so eine Variable pro Übersetzungseinheit.

In welcher Beziehung steht Statik zur Verknüpfung einer Variablen?

Es gibt eine interne Verknüpfung von Variablen mit Namespace-Gültigkeitsbereich. Es gibt Mitgliedern und lokalen Variablen statische Speicherdauer.

Lassen Sie uns das alles erweitern:

//

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
}

Dieses ganze statische Schlüsselwort ist geradezu verwirrend

Auf jeden Fall, es sei denn, Sie sind damit vertraut. :) Um zu vermeiden, dass der Sprache neue Schlüsselwörter hinzugefügt werden, hat das Komitee diese IMO zu diesem Zweck wiederverwendet - Verwirrung. Es wird verwendet, um verschiedene Dinge zu bezeichnen (könnte ich sagen, wahrscheinlich gegensätzliche Dinge).

Luchian Grigore
quelle
1
Lassen Sie mich das klarstellen - Sie sagen, wenn ich static int xim Namespace-Bereich sage , gibt es dann nicht statischen Speicher?
Michael Hagar
30

Um die Frage zu klären, würde ich die Verwendung des Schlüsselworts "statisch" lieber in drei verschiedene Formen einteilen:

(EIN). Variablen

(B). Funktionen

(C). Mitgliedsvariablen / Funktionen von Klassen

Die folgende Erklärung folgt für jede der Unterüberschriften:

(A) 'statisches' Schlüsselwort für Variablen

Dieser kann etwas knifflig sein, aber wenn er richtig erklärt und verstanden wird, ist er ziemlich einfach.

Um dies zu erklären, ist es zunächst sehr nützlich, den Umfang, die Dauer und die Verknüpfung von Variablen zu kennen, ohne die das trübe Konzept des staischen Schlüsselworts immer nur schwer zu erkennen ist

1. Bereich : Legt fest, wo in der Datei auf die Variable zugegriffen werden kann. Es kann zwei Arten geben: (i) Lokaler oder Blockbereich . (ii) Globaler Geltungsbereich

2. Dauer : Legt fest, wann eine Variable erstellt und zerstört wird. Es gibt wieder zwei Arten: (i) Automatische Speicherdauer (für Variablen mit lokalem oder Blockbereich). (ii) Statische Speicherdauer (für Variablen mit globalem Gültigkeitsbereich oder lokale Variablen (in einer Funktion oder einem Codeblock) mit statischem Bezeichner).

3. Verknüpfung : Legt fest, ob auf eine Variable in einer anderen Datei zugegriffen (oder verknüpft) werden kann. Wieder (und zum Glück) gibt es zwei Arten: (i) Interne Verknüpfung (für Variablen mit Blockbereich und globalem Bereich / Dateibereich / globaler Namespace-Bereich) (ii) Externe Verknüpfung (für Variablen mit nur globalem Bereich / Dateibereich / Globaler Namespace-Bereich)

Im Folgenden finden Sie ein Beispiel zum besseren Verständnis einfacher globaler und lokaler Variablen (keine lokalen Variablen mit statischer Speicherdauer):

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

Jetzt kommt das Konzept der Verknüpfung. Wenn eine in einer Datei definierte globale Variable in einer anderen Datei verwendet werden soll, spielt die Verknüpfung der Variablen eine wichtige Rolle.

Die Verknüpfung globaler Variablen wird durch die Schlüsselwörter (i) statisch und (ii) extern angegeben

(Jetzt bekommst du die Erklärung)

Das statische Schlüsselwort kann auf Variablen mit lokalem und globalem Gültigkeitsbereich angewendet werden. In beiden Fällen bedeuten sie unterschiedliche Bedeutungen. Ich werde zuerst die Verwendung des Schlüsselworts 'static' in Variablen mit globalem Gültigkeitsbereich erläutern (wobei ich auch die Verwendung des Schlüsselworts 'extern' erläutere) und später die Verwendung für Schlüsselwörter mit lokalem Gültigkeitsbereich.

1. Statisches Schlüsselwort für Variablen mit globalem Gültigkeitsbereich

Globale Variablen haben eine statische Dauer, dh sie verlassen nicht den Gültigkeitsbereich, wenn ein bestimmter Codeblock (z. B. main ()), in dem er verwendet wird, endet. Abhängig von der Verknüpfung kann auf sie entweder nur innerhalb derselben Datei zugegriffen werden, in der sie deklariert sind (für statische globale Variablen), oder außerhalb der Datei, sogar außerhalb der Datei, in der sie deklariert sind (globale Variablen vom externen Typ).

Im Fall einer globalen Variablen mit externem Bezeichner und wenn auf diese Variable außerhalb der Datei zugegriffen wird, in der sie initialisiert wurde, muss sie in der Datei, in der sie verwendet wird, vorwärts deklariert werden, genau wie eine Funktion weitergeleitet werden muss deklariert, wenn sich die Definition in einer anderen Datei befindet als der, in dem sie verwendet wird.

Wenn die globale Variable dagegen ein statisches Schlüsselwort hat, kann sie nicht in einer Datei verwendet werden, außerhalb derer sie deklariert wurde.

(Zur Verdeutlichung siehe Beispiel unten)

z.B:

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

Jetzt kann jede Variable in c ++ entweder eine Konstante oder eine Nicht-Konstante sein, und für jede 'Konstanz' erhalten wir zwei Fälle von Standard-C ++ - Verknüpfung, falls keine angegeben ist:

(i) Wenn eine globale Variable nicht const ist, ist ihre Verknüpfung standardmäßig extern , dh auf die globale Variable nicht const kann in einer anderen CPP-Datei durch Vorwärtsdeklaration unter Verwendung des Schlüsselworts extern (mit anderen Worten non const global) zugegriffen werden Variablen haben eine externe Verknüpfung (natürlich mit statischer Dauer). Auch die Verwendung des externen Schlüsselworts in der Originaldatei, in der es definiert wurde, ist redundant. Verwenden Sie in diesem Fall den Bezeichner 'static' vor dem Variablentyp, um eine nicht konstante globale Variable für externe Dateien unzugänglich zu machen .

(ii) Wenn eine globale Variable const ist, ist ihre Verknüpfung standardmäßig statisch , dh auf eine globale const-Variable kann in einer anderen Datei als der definierten nicht zugegriffen werden (mit anderen Worten, globale const-Variablen haben eine interne Verknüpfung (mit statischer Dauer) natürlich)). Auch die Verwendung eines statischen Schlüsselworts, um zu verhindern, dass auf eine globale Variable const in einer anderen Datei zugegriffen wird, ist redundant. Verwenden Sie hier den Bezeichner 'extern' vor dem Typ der Variablen , um eine konstante globale Variable mit einer externen Verknüpfung zu versehen

Hier ist eine Zusammenfassung für globale Bereichsvariablen mit verschiedenen Verknüpfungen

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

Als nächstes untersuchen wir, wie sich die oben genannten globalen Variablen verhalten, wenn auf sie in einer anderen Datei zugegriffen wird.

//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. Statisches Schlüsselwort für Variablen mit lokalem Bereich

Aktualisierungen (August 2019) des statischen Schlüsselworts für Variablen im lokalen Bereich

Dies kann weiter in zwei Kategorien unterteilt werden:

(i) statisches Schlüsselwort für Variablen innerhalb eines Funktionsblocks und (ii) statisches Schlüsselwort für Variablen innerhalb eines unbenannten lokalen Blocks.

(i) statisches Schlüsselwort für Variablen innerhalb eines Funktionsblocks.

Ich habe bereits erwähnt, dass Variablen mit lokalem Gültigkeitsbereich eine automatische Dauer haben, dh sie entstehen, wenn der Block eingegeben wird (sei es ein normaler Block, sei es ein Funktionsblock), und hören auf zu existieren, wenn der Block endet, kurz gesagt, Variablen mit lokalem Gültigkeitsbereich haben automatische Dauer und automatische Dauervariablen (und Objekte) haben keine Verknüpfung, was bedeutet, dass sie außerhalb des Codeblocks nicht sichtbar sind.

Wenn ein statischer Bezeichner auf eine lokale Variable innerhalb eines Funktionsblocks angewendet wird, ändert er die Dauer der Variablen von automatisch in statisch und ihre Lebensdauer entspricht der gesamten Dauer des Programms. Dies bedeutet, dass sie einen festen Speicherort hat und ihr Wert nur initialisiert wird einmal vor dem Programmstart wie in der CPP-Referenz angegeben (Initialisierung sollte nicht mit Zuweisung verwechselt werden)

Schauen wir uns ein Beispiel an.

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

Wenn man das obige Kriterium für statische lokale Variablen und statische globale Variablen betrachtet, könnte man versucht sein zu fragen, was der Unterschied zwischen ihnen sein könnte. Während die globalen Variablen an jedem Punkt in dem Code innerhalb zugänglich sind (in gleichen als auch unterschiedlichen Übersetzungseinheit in Abhängigkeit von der const -ness und extern -ness), ist eine statische Variable innerhalb eines Funktionsblocks definiert , die nicht direkt zugänglich sind . Die Variable muss durch den Funktionswert oder die Referenz zurückgegeben werden. Lassen Sie uns dies anhand eines Beispiels demonstrieren:

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

Weitere Erläuterungen zur Auswahl der statischen globalen und statischen lokalen Variablen finden Sie in diesem Stackoverflow-Thread

(ii) statisches Schlüsselwort für Variablen innerhalb eines unbenannten lokalen Blocks.

Auf statische Variablen innerhalb eines lokalen Blocks (kein Funktionsblock) kann außerhalb des Blocks nicht zugegriffen werden, sobald der lokale Block den Gültigkeitsbereich verlässt. Keine Einschränkungen dieser Regel.

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

In C ++ 11 wurde das Schlüsselwort eingeführt, constexprdas die Auswertung eines Ausdrucks zur Kompilierungszeit garantiert und es dem Compiler ermöglicht, den Code zu optimieren. Wenn nun der Wert einer statischen const-Variablen innerhalb eines Bereichs zur Kompilierungszeit bekannt ist, wird der Code auf ähnliche Weise wie bei optimiert constexpr. Hier ist ein kleines Beispiel

Ich empfehle den Lesern auch, den Unterschied zwischen constexprund static constfür Variablen in diesem Stackoverflow-Thread nachzuschlagen . Damit ist meine Erklärung für das statische Schlüsselwort abgeschlossen, das auf Variablen angewendet wird.

B. 'statisches' Schlüsselwort für Funktionen

In Bezug auf Funktionen hat das statische Schlüsselwort eine einfache Bedeutung. Hier bezieht es sich auf die Verknüpfung der Funktion Normalerweise haben alle in einer CPP-Datei deklarierten Funktionen standardmäßig eine externe Verknüpfung, dh eine in einer Datei definierte Funktion kann in einer anderen CPP-Datei durch Vorwärtsdeklaration verwendet werden.

Die Verwendung eines statischen Schlüsselworts vor der Funktionsdeklaration beschränkt die Verknüpfung mit intern , dh eine statische Funktion kann nicht innerhalb einer Datei außerhalb ihrer Definition verwendet werden.

C. Staitc-Schlüsselwort, das für Elementvariablen und Funktionen von Klassen verwendet wird

1. Schlüsselwort 'static' für Mitgliedsvariablen von Klassen

Ich beginne hier direkt mit einem Beispiel

#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 diesem Beispiel behält die statische Variable m_designNum ihren Wert bei, und diese einzelne private Mitgliedsvariable (da sie statisch ist) wird s / w mit allen Variablen des Objekttyps DesignNumber geteilt

Ebenso wie andere Elementvariablen sind statische Elementvariablen einer Klasse keinem Klassenobjekt zugeordnet, was durch das Drucken von anyNumber in der Hauptfunktion demonstriert wird

const vs nicht-const statische Elementvariablen in der Klasse

(i) statische Elementvariablen der Klasse " Nicht-Konstante" Im vorherigen Beispiel waren die statischen Elemente (sowohl öffentliche als auch private) Nichtkonstanten. Der ISO-Standard verbietet die Initialisierung von nicht konstanten statischen Elementen in der Klasse. Daher müssen sie wie im vorherigen Beispiel nach der Klassendefinition initialisiert werden, mit der Einschränkung, dass das statische Schlüsselwort weggelassen werden muss

(ii) const-statische Elementvariablen der Klasse Dies ist unkompliziert und entspricht der Konvention der Initialisierung anderer const-Elementvariablen, dh die konstanten statischen Elementvariablen einer Klasse können zum Zeitpunkt der Deklaration initialisiert und am Ende initialisiert werden der Klassendeklaration mit einer Einschränkung, dass das Schlüsselwort const dem statischen Element hinzugefügt werden muss, wenn es nach der Klassendefinition initialisiert wird.

Ich würde jedoch empfehlen, die statischen Elementvariablen const am Deklarationspunkt zu initialisieren. Dies entspricht der Standard-C ++ - Konvention und lässt den Code sauberer aussehen

Weitere Beispiele für statische Elementvariablen in einer Klasse finden Sie unter folgendem Link unter learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. Schlüsselwort 'static' für die Mitgliedsfunktion von Klassen

Genau wie Mitgliedsvariablen von Klassen statisch sein können, können auch Mitgliedsfunktionen von Klassen statisch sein. Normale Elementfunktionen von Klassen sind immer einem Objekt des Klassentyps zugeordnet. Im Gegensatz dazu sind statische Elementfunktionen einer Klasse keinem Objekt der Klasse zugeordnet, dh sie haben keinen * diesen Zeiger.

Zweitens, da die statischen Elementfunktionen der Klasse keinen * diesen Zeiger haben, können sie mit dem Operator Klassenname und Bereichsauflösung in der Hauptfunktion (ClassName :: functionName ();) aufgerufen werden.

Drittens können statische Elementfunktionen einer Klasse nur auf statische Elementvariablen einer Klasse zugreifen, da nicht statische Elementvariablen einer Klasse zu einem Klassenobjekt gehören müssen.

Weitere Beispiele für statische Elementfunktionen in einer Klasse finden Sie unter folgendem Link von learncpp.com

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

ggulgulia
quelle
1
1) Vor c ++ 17 können nur integrale statische const-Member-Variablen in der Klasse initialisiert werden, z. B. struct Foo{static const std::string name = "cpp";};ist ein Fehler, nameder außerhalb der Klasse definiert werden muss. Mit Inline-Variablen, die in c ++ 17 eingeführt wurden, kann Folgendes codiert werden: struct Foo{static inline const std::string name = "cpp";};2) Auf öffentliche statische Member- / Member-Funktionen kann über den Klassennamen mit dem Bereichsauflösungsoperator und auch über eine Instanz mit dem Punktoperator zugegriffen werden (z. B. instance.some_static_method ())
oz1
Sollte "m_anyVariable" nicht zu "m_anyNumber" werden? in Ihrem letzten Codebeispiel?
Gebbissimo
Ich kann die Vollständigkeit und Richtigkeit der Antwort nicht beurteilen, aber sie scheint wirklich umfassend zu sein und war leicht zu befolgen. Vielen Dank! Wenn Sie es verbessern möchten, kann eine kurze Zusammenfassung am Anfang hilfreich sein, da es sich um einen ziemlich langen Text handelt und die Hauptpunkte leicht als verschachtelte Liste oder als Baumdiagramm für Personen dargestellt werden können, die Begriffe wie "intern / extern" kennen Verknüpfung "
gebbissimo
18

Es ist eigentlich ganz einfach. Wenn Sie eine Variable im Bereich einer Funktion als statisch deklarieren, bleibt ihr Wert zwischen aufeinanderfolgenden Aufrufen dieser Funktion erhalten. So:

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

wird 678statt angezeigt 666, weil es sich an den inkrementierten Wert erinnert.

Die statischen Elemente behalten ihren Wert über Instanzen der Klasse hinweg bei. Also der folgende Code:

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

gibt 4 aus, da first.a und second.a im Wesentlichen dieselbe Variable sind. Informationen zur Initialisierung finden Sie in dieser Frage.

Maciej Stachowski
quelle
Dies betrifft keine Namespace-Bereichsvariablen.
Michael Hagar
10

Wenn Sie eine staticVariable im Dateibereich deklarieren , ist diese Variable nur in dieser bestimmten Datei verfügbar (technisch gesehen die * Übersetzungseinheit, aber lassen Sie uns dies nicht zu sehr komplizieren). Beispielsweise:

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

Für eine lokale Variable staticbedeutet dies, dass die Variable mit Null initialisiert wird und ihren Wert zwischen den Aufrufen beibehält:

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

Für Klassenvariablen bedeutet dies, dass es nur eine einzige Instanz dieser Variablen gibt, die von allen Mitgliedern dieser Klasse gemeinsam genutzt wird. Abhängig von den Berechtigungen kann auf die Variable von außerhalb der Klasse unter Verwendung ihres vollständig qualifizierten Namens zugegriffen werden.

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

Durch Markieren einer Nichtklassenfunktion als staticwird die Funktion nur von dieser Datei aus zugänglich und von anderen Dateien aus nicht zugänglich.

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

Wenn Sie sie als Klassenmitgliedsfunktionen markieren, staticbedeutet dies, dass die Funktion nicht für eine bestimmte Instanz eines Objekts aufgerufen werden muss (dh keinen thisZeiger hat).

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
quelle
8

Statische Variablen werden von jeder Instanz einer Klasse gemeinsam genutzt, anstatt dass jede Klasse eine eigene Variable hat.

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;

Jede Instanz von 'MyClass' hat ihre eigene 'myVar', teilt jedoch dieselbe 'myStaticVar'. Tatsächlich benötigen Sie nicht einmal eine Instanz von MyClass, um auf 'myStaticVar' zuzugreifen, und Sie können außerhalb der Klasse wie folgt darauf zugreifen:

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

Bei Verwendung innerhalb einer Funktion als lokale Variable (und nicht als Klassenmitgliedsvariable) bewirkt das statische Schlüsselwort etwas anderes. Sie können eine persistente Variable erstellen, ohne einen globalen Bereich anzugeben.

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

Es ist eine globale Variable in Bezug auf die Persistenz ... aber ohne global in Bezug auf Umfang / Zugänglichkeit.

Sie können auch statische Elementfunktionen haben. Statische Funktionen sind im Grunde Funktionen, die keine Mitglieder sind, jedoch im Namespace des Klassennamens und mit privatem Zugriff auf die Mitglieder der Klasse.

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

Wenn Sie eine Mitgliedsfunktion aufrufen, gibt es einen versteckten Parameter namens 'this', der ein Zeiger auf die Instanz der Klasse ist, die die Funktion aufruft. Statische Elementfunktionen nicht haben , dass versteckte Parameter ... sie ohne eine Klasseninstanz aufrufbar sind, aber auch nicht zugreifen kann nicht statische Membervariablen einer Klasse, weil sie mit keinen ‚this‘ Zeiger auf Arbeit haben. Sie werden nicht für eine bestimmte Klasseninstanz aufgerufen.

Jamin Gray
quelle
1
"Vorausgesetzt, es ist öffentlich zugänglich." - es ist nicht.
Luchian Grigore
2
myStaticVarmuss auch definiert werden. Ein bisschen wichtig zu erwähnen, dass Sie bei der Beantwortung einer Frage zur Semantik des staticSchlüsselworts nicht denken?
Prätorianer
@Praetorian: Danke, behoben.
Jamin Gray
1
@JaminGrey Mit "statisch eigenständig" habe ich statische Nichtmitgliedsfunktionen gemeint, und ich schreibe solche, wenn ich nur in der aktuellen CPP-Datei neue Funktionen benötige und nicht möchte, dass der Linker ein zusätzliches Symbol verarbeiten muss.
VR
1
@ VR ungerade! Ich wusste nie, dass es Funktionalität gibt . Vielen Dank für die Erweiterung meines Wissens!
Jamin Gray
1

Ich bin kein C-Programmierer, daher kann ich Ihnen keine Informationen über die Verwendung von Statik in einem C-Programm geben, aber wenn es um objektorientierte Programmierung geht, deklariert Statik grundsätzlich eine Variable, eine Funktion oder eine Klasse als gleich während der gesamten Laufzeit des Programms. Nehmen Sie zum Beispiel.

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

Wenn Sie diese Klasse in Ihrem Main instanziieren, machen Sie so etwas.

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

Diese beiden Klasseninstanzen unterscheiden sich vollständig voneinander und arbeiten unabhängig voneinander. Aber wenn Sie die Klasse A so neu erstellen würden.

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

Kehren wir noch einmal zur Hauptstraße zurück.

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

Dann würden a1 und a2 dieselbe Kopie von int x teilen, wobei alle Operationen an x ​​in a1 die Operationen von x in a2 direkt beeinflussen würden. Also, wenn ich das tun würde

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
}

Beide Instanzen der Klasse A teilen statische Variablen und Funktionen. Hoffe das beantwortet deine Frage. Aufgrund meiner begrenzten Kenntnisse in C kann ich sagen, dass das Definieren einer Funktion oder Variablen als statisch bedeutet, dass nur für die Datei sichtbar ist, in der die Funktion oder Variable als statisch definiert ist. Dies sollte jedoch besser von einem C-Mitarbeiter und nicht von mir beantwortet werden. C ++ ermöglicht sowohl C- als auch C ++ - Möglichkeiten, Ihre Variablen als statisch zu deklarieren, da es vollständig abwärtskompatibel mit C ist.

David Tr
quelle
1

Was bedeutet das mit lokaler Variable? Ist das eine lokale Funktionsvariable?

Ja - Nicht global, z. B. eine lokale Funktionsvariable.

Denn wenn Sie eine lokale Funktion als statisch deklarieren, wird sie beim ersten Aufrufen dieser Funktion nur einmal initialisiert.

Richtig.

Es wird auch nur über die Speicherdauer in Bezug auf Klassenmitglieder gesprochen. Was ist damit, dass es nicht instanzspezifisch ist, das ist auch eine Eigenschaft von statischem Nein? Oder ist das Lagerdauer?

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

Das heißt, alle Instanzen von RShare int R::a- int R::awerden niemals kopiert.

Was ist nun mit dem Fall mit statischem und Dateibereich?

Tatsächlich ein Global, der gegebenenfalls über einen Konstruktor / Destruktor verfügt - die Initialisierung wird erst nach dem Zugriff verschoben.

In welcher Beziehung steht Statik zur Verknüpfung einer Variablen?

Für eine lokale Funktion ist sie extern. Zugriff: Es ist für die Funktion zugänglich (es sei denn, Sie geben es zurück).

Für eine Klasse ist es extern. Zugriff: Es gelten Standardzugriffsspezifizierer (öffentlich, geschützt, privat).

static kann auch eine interne Verknüpfung angeben, je nachdem, wo sie deklariert ist (Datei / Namespace).

Dieses ganze statische Schlüsselwort ist geradezu verwirrend

Es hat zu viele Zwecke in C ++.

Kann jemand die verschiedenen Verwendungszwecke für Englisch klären und mir auch sagen, wann ein statisches Klassenmitglied initialisiert werden soll?

Es wird vorher automatisch initialisiert, mainwenn es geladen ist und einen Konstruktor hat. Das mag sich nach einer guten Sache anhören, aber die Reihenfolge der Initialisierung liegt weit außerhalb Ihrer Kontrolle. Daher ist es sehr schwierig, die komplexe Initialisierung aufrechtzuerhalten, und Sie möchten dies minimieren. Wenn Sie eine statische Aufladung benötigen, funktionieren lokale Skalen in Bibliotheken und Bibliotheken viel besser Projekte. Bei Daten mit statischer Speicherdauer sollten Sie versuchen, dieses Design zu minimieren, insbesondere wenn es veränderbar ist (globale Variablen). Die Initialisierungszeit variiert auch aus einer Reihe von Gründen: Der Loader und der Kernel haben einige Tricks, um den Speicherbedarf zu minimieren und die Initialisierung abhängig von den betreffenden Daten zu verschieben.

Justin
quelle
1

Statisches Objekt: Wir können statische Klassenmitglieder mit dem Schlüsselwort static definieren. Wenn wir ein Mitglied einer Klasse als statisch deklarieren, bedeutet dies, dass unabhängig davon, wie viele Objekte der Klasse erstellt werden, nur eine Kopie des statischen Elements vorhanden ist.

Ein statisches Element wird von allen Objekten der Klasse gemeinsam genutzt. Alle statischen Daten werden beim Erstellen des ersten Objekts auf Null initialisiert, wenn keine andere Initialisierung vorhanden ist. Wir können es nicht in die Klassendefinition einfügen, aber es kann außerhalb der Klasse initialisiert werden, wie im folgenden Beispiel beschrieben, indem die statische Variable mithilfe des Bereichsauflösungsoperators :: neu deklariert wird, um zu identifizieren, zu welcher Klasse sie gehört.

Versuchen wir das folgende Beispiel, um das Konzept der statischen Datenelemente zu verstehen:

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

Wenn der obige Code kompiliert und ausgeführt wird, führt dies zu folgendem Ergebnis:

Constructor called.
Constructor called.
Total objects: 2

Statische Funktionselemente: Indem Sie ein Funktionselement als statisch deklarieren, machen Sie es unabhängig von einem bestimmten Objekt der Klasse. Eine statische Elementfunktion kann auch dann aufgerufen werden, wenn keine Objekte der Klasse vorhanden sind und auf die statischen Funktionen nur mit dem Klassennamen und dem Bereichsauflösungsoperator :: zugegriffen wird.

Eine statische Elementfunktion kann nur von außerhalb der Klasse auf statische Datenelemente, andere statische Elementfunktionen und andere Funktionen zugreifen.

Statische Elementfunktionen haben einen Klassenbereich und keinen Zugriff auf diesen Zeiger der Klasse. Sie können eine statische Elementfunktion verwenden, um festzustellen, ob einige Objekte der Klasse erstellt wurden oder nicht.

Versuchen wir das folgende Beispiel, um das Konzept der statischen Funktionselemente zu verstehen:

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

Wenn der obige Code kompiliert und ausgeführt wird, führt dies zu folgendem Ergebnis:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
Nima Soroush
quelle
1
Es wäre richtig zu erwähnen, dass diese Paradigmen von tutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG