Bis zum Tod beschäftigen wir uns ganz oder teilweise mit Statik in C ++





Hallo. Bei einer der Codeüberprüfungen bin ich auf die Idee gekommen, dass viele und was ich verstecken soll, nicht, dass wir gut verstehen, wann wir das statische Schlüsselwort verwenden sollen . In diesem Artikel möchte ich mein Wissen und meine Informationen zum statischen Schlüsselwort weitergeben . , , ++. /++ . , static ++, . ++, , , , .



static?



Statisch ist ein C ++ - Schlüsselwort, mit dem einem Element ein besonderes Merkmal zugewiesen wird. Bei statischen Elementen wird der Speicher nur einmal zugewiesen, und diese Elemente sind vorhanden, bis das Programm beendet wird. Alle diese Elemente werden nicht im Heap oder auf dem Stapel gespeichert, sondern in speziellen Speichersegmenten namens .data und .bss (abhängig davon, ob statische Daten initialisiert werden oder nicht). Das Bild unten zeigt ein typisches Layout für den Programmspeicher.





Wo wird es verwendet?



Unten sehen Sie ein Diagramm, wie und wo Statik in einem Programm verwendet wird.







Und jetzt werde ich versuchen, alles, was im Diagramm gezeigt wird, detailliert zu beschreiben. Gehen!



Statische Variablen innerhalb einer Funktion



Statische Variablen werden bei Verwendung innerhalb einer Funktion nur einmal initialisiert und behalten dann ihren Wert. Diese statischen Variablen werden in einem statischen Speicherbereich ( .data oder .bss ) und nicht auf dem Stapel gespeichert, sodass der Wert der Variablen während der gesamten Lebensdauer des Programms gespeichert und verwendet werden kann. Schauen wir uns zwei nahezu identische Programme und ihr Verhalten an. Der einzige Unterschied besteht darin, dass einer eine statische Variable verwendet und der andere nicht.



Erstes Programm:



#include <iostream>

void counter() {
  static int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


Programmausgabe:

0123456789


Zweites Programm:



#include <iostream>

void counter() {
  int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


Programmausgabe:

000000000


Wenn Sie in Zeile 4 nicht statisch verwenden , erfolgt die Speicherzuweisung und Initialisierung der Variablenanzahl bei jedem Aufruf von counter () und wird bei jedem Beenden der Funktion zerstört. Wenn wir die Variable jedoch statisch machen, wird nach der Initialisierung (beim ersten Aufruf der Funktion counter () ) die Zählung auf das Ende der Funktion main () beschränkt , und die Variable behält ihren Wert zwischen den Aufrufen von counter () bei .



Statische Klassenobjekte



Ein statisches Objekt einer Klasse hat die gleichen Eigenschaften wie eine oben beschriebene reguläre statische Variable, d. H. im .data- oder .bss- Speichersegment gespeichert , beim Start erstellt und beim Programmabbruch zerstört und nur einmal initialisiert. Das Objekt wird wie gewohnt initialisiert - über den Klassenkonstruktor. Betrachten wir ein Beispiel mit einem statischen Klassenobjekt.



#include <iostream>

class Base { //  3
public:
  Base() { //  5
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { //  8
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  static Base obj; //  14
} //  15

int main() {
  foo(); //  18
  std::cout << "End of main()" << std::endl;
  return 0;
}


Programmausgabe:

Konstruktor

Ende des Haupt- ()

Destruktors


3 Base ( 5) ( 8). . 14 obj Base. foo() 18.



- , , foo() 15, , .. . , , .





#include <iostream>

class Base {
public:
  Base() {
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { 
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  Base obj; 
} //  15

int main() {
  foo();
  std::cout << "End of main()" << std::endl;
  return 0;
}


Wenn wir beim Erstellen einer Variablen in der Funktion foo () statische Aufladung entfernen , wird das Objekt bei jedem Aufruf der Funktion in Zeile 15 zerstört . In diesem Fall wird die Ausgabe des Programms für eine lokale Variable mit zugewiesenem Speicher auf dem Stapel durchaus erwartet:

Konstruktor

Destruktor

Ende von main ()




Statische Klassenmitglieder



Im Vergleich zu früheren Anwendungsfällen sind statische Mitglieder einer Klasse etwas schwieriger zu verstehen. Mal sehen warum. Angenommen, wir haben das folgende Programm:



#include <iostream>

class A { //  3
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B { //  9
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

int main() {
  B b; //  19
  return 0;
}


In unserem Beispiel haben wir Klasse A (Zeile 3) und Klasse B (Zeile 9) mit statischen Klassenmitgliedern ( Zeile 15 ) erstellt. Wir gehen davon aus, dass beim Erstellen von Objekt b in Zeile 19 das Objekt a in Zeile 15 erstellt wird . Dies wäre der Fall, wenn wir nicht statische Mitglieder der Klasse verwenden würden. Die Ausgabe des Programms wird jedoch wie folgt sein:

Konstruktor B

Destruktor B.


Der Grund für dieses Verhalten ist, dass die statischen Elemente einer Klasse nicht mit einem Konstruktor initialisiert werden, da sie nicht von der Initialisierung des Objekts abhängen. Jene. In Zeile 15 deklarieren wir das Objekt nur und definieren es nicht, da die Definition außerhalb der Klasse mit dem Bereichsauflösungsoperator (: :) erfolgen muss . Lassen Sie uns definieren eine Klasse B - Mitglieder .



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

A B::a; //  18 ()

int main() {
  B b;
  return 0;
}


Nachdem wir unser statisches Klassenmitglied in Zeile 18 definiert haben, können wir die folgende Programmausgabe sehen:

Konstruktor A

Konstruktor B

Destruktor B

Destruktor A.


Es muss daran erinnert werden, dass das Klassenmitglied für alle Instanzen der Klasse B gleich ist , d. H. Wenn wir drei Objekte der Klasse B erstellt haben , wird der Konstruktor des statischen Klassenmitglieds nur einmal aufgerufen. Hier ist ein Beispiel dafür, wovon ich spreche:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << count++ << std::endl; }
  ~B() { std::cout << "Destructor B" << --count << std::endl; }

private:
  static A a; // 
  static int count; // 
};

A B::a; // 
int B::count = 1; // 

int main() {
  B b1, b2, b3;
  return 0;
}


Programmausgabe:

Konstruktor A

Konstruktor B1

Konstruktor B2

Konstruktor B3

Destruktor B3

Destruktor B2

Destruktor B1

Destruktor A.


Statische Funktionen



Statische Funktionen kamen von C zu C ++. Standardmäßig sind alle Funktionen in C global. Wenn Sie zwei Funktionen mit demselben Namen in zwei verschiedenen .c-Dateien (.cpp) desselben Projekts erstellen möchten, wird eine Fehlermeldung angezeigt, die besagt, dass diese Funktion vorhanden ist bereits definiert ( schwerwiegender Fehler LNK1169: ein oder mehrere mehrfach definierte Symbole gefunden ). Unten finden Sie eine Liste von drei Dateien eines Programms.



// extend_math.cpp
int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


// math.cpp
int sum(int a, int b) {
  return a + b;
}


// main.cpp
 int sum(int, int); // declaration

int main() {
  int result = sum(1, 2);
  return 0;
}


Um dieses Problem zu beheben, deklarieren wir eine der Funktionen als statisch. Zum Beispiel dieses:



// extend_math.cpp
static int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


, , . sum() math.cpp . , static , , , , , (.h).



, inline static, , . (.cpp), #include , . , , .. include .cpp .





- ()



Sie können eine statische Elementfunktion verwenden, ohne ein Objekt der Klasse zu erstellen. Auf statische Funktionen wird mit dem Klassennamen und dem Bereichsauflösungsoperator (: :) zugegriffen . Bei Verwendung einer statischen Elementfunktion gibt es Einschränkungen wie:



  1. Innerhalb einer Funktion können Sie nur von außerhalb der Klasse auf statische Datenelemente, andere statische Elementfunktionen und andere Funktionen zugreifen.
  2. Statische Elementfunktionen haben den Umfang der Klasse, in der sie sich befinden.
  3. Sie haben keinen Zugriff auf diesen Zeiger der Klasse, da wir kein Objekt zum Aufrufen dieser Funktion erstellen.


Schauen wir uns das folgende Beispiel an:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }

  static void foo() { //  8
    std::cout << "static foo()" << std::endl;
  }
};

int main() {
  A::foo(); //  14
  return 0;
}


In Klasse A haben wir in Zeile 8 eine statische Elementfunktion foo () . In Zeile 14 rufen wir die Funktion mit dem Klassennamen und dem Bereichsauflösungsoperator auf und erhalten die folgende Programmausgabe:

statisches foo ()


An der Ausgabe können Sie erkennen, dass keine Objekterstellung erfolgt und kein Konstruktor / Destruktor aufgerufen wird.



Wenn die foo () -Methode nicht statisch wäre, würde der Compiler einen Fehler in den Ausdruck in Zeile 14 werfen , weil Sie müssen ein Objekt erstellen, um auf seine nicht statischen Methoden zugreifen zu können.





Fazit



« static , ». , , .



:



  • , . , , , . , , .
  • , , .. , . , , .. .
  • static Singleton, , . , -. Singleton , , .
  • Manchmal werden statische Variablen verwendet, damit eine Funktion nur einmal ausgeführt wird, ohne den vorherigen Status irgendwo im Objekt zu speichern. Ein Beispiel finden Sie im Abschnitt "Statische Variablen innerhalb einer Funktion". Dies ist jedoch kein sehr guter Ansatz und kann zu langen Stunden der Fehlersuche führen, wenn Sie Multithreading verwenden.
  • In der Praxis verwenden C ++ - Programmierer häufig statische Elementfunktionen als Alternative zu regulären Funktionen, für deren Ausführung kein Objekt erstellt werden muss.


Ich hoffe, Ihnen hat mein Artikel über das statische Schlüsselwort in C ++ gefallen. Ich würde mich über jede Kritik und jeden Rat freuen. Danke an alle!



All Articles