Jusqu'à la mort, faisons-nous partie ou tout de la statique en C ++





Bonjour. Lors de l'une des revues de code, je suis tombé sur l'idée que beaucoup, et quoi me cacher, non pas que nous comprenions bien quand utiliser le mot-clé statique . Dans cet article, je souhaite partager mes connaissances et mes informations concernant le mot-clé statique . , , ++. /++ . , static ++, . ++, , , , .



static?



Static est un mot-clé C ++ utilisé pour donner à un élément une caractéristique spéciale. Pour les éléments statiques, la mémoire n'est allouée qu'une seule fois et ces éléments existent jusqu'à la fin du programme. Tous ces éléments ne sont pas stockés dans le tas ou sur la pile, mais dans des segments de mémoire spéciaux appelés .data et .bss (selon que les données statiques sont initialisées ou non). L'image ci-dessous montre une disposition typique de la mémoire programme.





Où est-il utilisé?



Vous trouverez ci-dessous un diagramme montrant comment et où la statique est utilisée dans un programme.







Et maintenant, je vais essayer de décrire en détail tout ce qui est montré dans le diagramme. Aller!



Variables statiques à l'intérieur d'une fonction



Les variables statiques, lorsqu'elles sont utilisées dans une fonction, ne sont initialisées qu'une seule fois, puis conservent leur valeur. Ces variables statiques sont stockées dans une zone de mémoire statique ( .data ou .bss ) plutôt que sur la pile, ce qui permet à la valeur de la variable d'être stockée et utilisée tout au long de la vie du programme. Jetons un coup d'œil à deux programmes presque identiques et à leur comportement. La seule différence est que l'un utilise une variable statique et l'autre pas.



Premier programme:



#include <iostream>

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

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


Sortie du programme:

0123456789


Deuxième programme:



#include <iostream>

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

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


Sortie du programme:

000000000


Si vous n'utilisez pas statique sur la ligne 4 , l'allocation de mémoire et l'initialisation de la variable count se produisent à chaque fois que counter () est appelé , et sont détruites à chaque fois que la fonction se termine. Mais si nous rendons la variable statique, après l'initialisation (la première fois que la fonction counter () est appelée ), count sera limité à la fin de la fonction main () , et la variable conservera sa valeur entre les appels à counter () .



Objets de classe statique



Un objet statique d'une classe a les mêmes propriétés qu'une variable statique régulière décrite ci-dessus, c'est-à-dire stocké dans un segment de mémoire .data ou .bss , créé au démarrage et détruit à la fin du programme, et initialisé une seule fois. L'objet est initialisé comme d'habitude - via le constructeur de classe. Prenons un exemple avec un objet de classe statique.



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


Sortie du programme:

Constructeur

Fin du

destructeur principal ()


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


Si nous supprimons static lors de la création d'une variable dans la fonction foo () , alors la destruction de l'objet se produira à la ligne 15 à chaque fois que la fonction est appelée. Dans ce cas, la sortie du programme sera tout à fait attendue pour une variable locale avec mémoire allouée sur la pile:

Constructeur

Destructeur

Fin de main ()




Membres de classe statiques



Par rapport aux cas d'utilisation précédents, les membres statiques d'une classe sont un peu plus difficiles à comprendre. Voyons pourquoi. Supposons que nous ayons le programme suivant:



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


Dans notre exemple, nous avons créé la classe A (ligne 3) et la classe B (ligne 9) avec des membres de classe statiques ( ligne 15 ). Nous supposons que la création de l'objet b à la ligne 19 créera l'objet a à la ligne 15 . Ce serait le cas si nous utilisions des membres non statiques de la classe. Mais la sortie du programme sera la suivante:

Constructeur B

Destructeur B


La raison de ce comportement est que les membres statiques d'une classe ne sont pas initialisés avec un constructeur car ils ne dépendent pas de l'initialisation de l'objet. Ceux. à la ligne 15, nous déclarons seulement l'objet, pas le définissant, car la définition doit se produire en dehors de la classe en utilisant l' opérateur de résolution de portée (: :) . Définissons un de la catégorie B .



#include <iostream>

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

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

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

A B::a; //  18 ()

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


Maintenant, après avoir défini notre membre de classe statique à la ligne 18, nous pouvons voir la sortie de programme suivante:

Constructeur A

Constructeur B

Destructeur B

Destructeur A


Il faut se rappeler que le membre de la classe sera le même pour toutes les instances de la classe B , c'est-à-dire si nous avons créé trois objets de classe B , alors le constructeur du membre de classe statique ne sera appelé qu'une seule fois. Voici un exemple de ce dont je parle:



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


Sortie du programme:

Constructeur A

Constructeur B1

Constructeur B2

Constructeur B3

Destructeur B3

Destructeur B2

Destructeur B1

Destructeur A


Fonctions statiques



Les fonctions statiques sont venues en C ++ depuis C. Par défaut, toutes les fonctions en C sont globales, et si vous voulez créer deux fonctions avec le même nom dans deux fichiers .c (.cpp) différents du même projet, vous obtiendrez une erreur indiquant que cette fonction déjà défini ( erreur fatale LNK1169: un ou plusieurs symboles multi-définis trouvés ). Vous trouverez ci-dessous une liste de trois fichiers d'un programme.



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


Afin de résoudre ce problème, nous déclarerons l'une des fonctions static. Par exemple celui-ci:



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





- ()



Vous pouvez utiliser une fonction membre statique sans créer d'objet de la classe. Les fonctions statiques sont accessibles à l'aide du nom de classe et de l' opérateur de résolution de portée (: :) . Lors de l'utilisation d'une fonction membre statique, il existe des limitations telles que:



  1. Dans une fonction, vous ne pouvez accéder qu'aux membres de données statiques, aux autres fonctions membres statiques et à toute autre fonction en dehors de la classe.
  2. Les fonctions membres statiques ont la portée de la classe dans laquelle elles résident.
  3. Vous n'avez pas accès au pointeur this de la classe, car nous ne créons aucun objet pour appeler cette fonction.


Jetons un œil à l'exemple suivant:



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


Dans la classe A, à la ligne 8 , nous avons une fonction membre statique foo () . À la ligne 14 , nous appelons la fonction en utilisant le nom de classe et l'opérateur de résolution de portée, et nous obtenons la sortie de programme suivante:

statique toto ()


À partir de la sortie, vous pouvez voir qu'il n'y a pas de création d'objet et qu'aucun constructeur / destructeur n'est appelé.



Si la méthode foo () n'était pas statique, le compilateur lèverait une erreur sur l'expression à la ligne 14 , car vous devez créer un objet pour accéder à ses méthodes non statiques.





Conclusion



« static , ». , , .



:



  • , . , , , . , , .
  • , , .. , . , , .. .
  • static Singleton, , . , -. Singleton , , .
  • Parfois, pour qu'une fonction ne s'exécute qu'une seule fois sans stocker l'état précédent quelque part dans l'objet, des variables statiques sont utilisées. Vous pouvez voir un exemple dans la section "Variables statiques à l'intérieur d'une fonction". Mais ce n'est pas une très bonne approche et peut conduire à de longues heures de recherche de bogues si vous utilisez le multithreading.
  • En pratique, les programmeurs C ++ utilisent souvent des fonctions membres statiques comme alternative aux fonctions régulières qui ne nécessitent pas la création d'un objet pour s'exécuter.


J'espère que vous avez apprécié mon article sur le mot clé static en C ++. Je serais heureux de toute critique et conseil. Merci à tous!



All Articles