This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:cpp:syntax:class:static [2019/10/09 13:54] bmonsuez |
in204:cpp:syntax:class:static [2022/11/18 10:50] (current) |
||
---|---|---|---|
Line 33: | Line 33: | ||
return Complex(rho*cos(theta), rho*sin(theta); | return Complex(rho*cos(theta), rho*sin(theta); | ||
} | } | ||
+ | }; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Complex complex = Complex::createFromPolar(1.41, O.785); | ||
+ | // Create a complex number from its polar coordinate. | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | La méthode ''createFromPolar'' est appellée non pas en passant une référence à une objet mais simplement en la préfixant par le nom de la classe ''Complex::''. Cette méthode ne reçoit aucune référence à l'instance de l'objet. De ce fait, le pointeur [[in204:cpp:syntax:class:this|''this'']] n'est pas accesible et il n'est pas non plus possible d'appeller d'autres méthodes que les méthodes statiques et d'accéder à d'autres champs qu'aux champs statiques. | ||
+ | |||
+ | |||
===== Les champs statiques ===== | ===== Les champs statiques ===== | ||
+ | |||
+ | Un champ //statique// est un champ qui est associé non pas à l'objet instance de la classe mais à la classe elle-même. Ceci signifie que le champ n'est alloué qu'une seule fois et ce pour toutes les instances de la classe. | ||
+ | |||
+ | Le champ //statique// est déclaré comme un champ habituel d'une classe à la différence que la déclaration doit être précédée par le mot-clé ''static''. | ||
+ | |||
+ | ==== La déclaration des champs statiques ==== | ||
+ | |||
+ | <code cpp> | ||
+ | #include<random> | ||
+ | |||
+ | class RandomGenerator | ||
+ | { | ||
+ | private: | ||
+ | static std::random_device m_seed; | ||
+ | static std::mt19937 m_generator; | ||
+ | |||
+ | public: | ||
+ | static UniformGenerator getUniform(int theMinValue, int theMaxValue); | ||
+ | static PoissonGenerator getPoisson(double theMean); | ||
+ | static int get() | ||
+ | { | ||
+ | return m_generator(); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // Classes dérivées | ||
+ | |||
+ | class UniformGenerator: public RandomGenerator | ||
+ | { | ||
+ | private: | ||
+ | sdt::uniform_int_distribution<> m_distribution; | ||
+ | public: | ||
+ | UniformGenerator(int theMinValue, int theMaxValue): | ||
+ | m_distribution(theMinValue, theMaxValue) | ||
+ | {} | ||
+ | int get() | ||
+ | { | ||
+ | return m_distribution(m_generator); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | class PoissonGenerator: public RandomGenerator | ||
+ | { | ||
+ | private: | ||
+ | sdt::poisson_distribution<> m_distribution; | ||
+ | public: | ||
+ | PoissonGenerator(double theMean): | ||
+ | m_distribution(theMean) | ||
+ | {} | ||
+ | int get() | ||
+ | { | ||
+ | return m_distribution(m_generator); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // Initialisation des champs ''statiques'' qui s'effectue en | ||
+ | // dehors de la classe. | ||
+ | |||
+ | RandomGenerator::m_seed; | ||
+ | RandomGenerator::m_generator(m_seed()); | ||
+ | |||
+ | // Définition des méthodes getUniform et | ||
+ | // getPoisson qui ne peuvent être définie | ||
+ | // qu'une fois la définition de ''UniformGenerator'' | ||
+ | // et ''PoissonGenerator'' ont été réalisée. | ||
+ | |||
+ | |||
+ | UniformGenerator RandomGenerator::getUniform(int theMinValue, int theMaxValue) | ||
+ | { | ||
+ | return UniformGenerator(theMinValue, theMaxValue); | ||
+ | } | ||
+ | PoissonGenerator RandomGenerator::getPoisson(double theMean) | ||
+ | { | ||
+ | return PoissonGenerator(theMean); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | L'exemple de code précédent montre comment déclarer les champs statiques : | ||
+ | |||
+ | <code cpp> | ||
+ | static std::random_device m_seed; | ||
+ | static std::mt19937 m_generator; | ||
+ | </code> | ||
+ | |||
+ | Pour ce faire, il suffit d'ajouter ''static'' devant. Cependant cela ne veut pas dire que les champs statiques sont créés et initialisés. Ils ne sont en fait que déclarés. Il faut ensuite les créer en dehors de la classe. | ||
+ | |||
+ | C'est ce que fait le code un peu plus loin : | ||
+ | |||
+ | <code cpp> | ||
+ | // Initialisation des champs ''statiques'' qui s'effectue en | ||
+ | // dehors de la classe. | ||
+ | |||
+ | RandomGenerator::m_seed; | ||
+ | RandomGenerator::m_generator(m_seed()); | ||
+ | </code> | ||
+ | |||
+ | Les variables ''RandomGenerator::m_seed'' et ''RandomGenerator::m_generator'' sont crées et initialisées. | ||
+ | |||
+ | Maintenant, ces variables peuvent-être accédées soit par l'ensemble des fonctions membres définies dans ''RandomGenerator'' mais aussi dans les classes dérivées de ''RandomGenerator'', dans notre cas ''UniformGenerator'' et ''PoissonGenerator''. Cependant, ''PoissonGenerator'' et ''UniformGenerator'' vont utiliser la même insitance de ''RandomGenerator::m_generator'', l'élément statique ne sera pas dupliqué mais sera partagé entre toutes les instances de ''RandomGenerator'' mais aussi de ses descendants. | ||
+ | Enfin, la méthode ''get'' de ''RandomGenerator'' qui est marquée ''static'' peut elle aussi accédée au champ statique ''RandomGenerator::m_seed''. | ||
+ | |||
+ | En fait malheureusement la situation n'est pas si simple. En effet, si la classe ''RandomGenerator'' est déclarée dans un fichier ''.hpp'', la déclaration : | ||
+ | |||
+ | <code cpp> | ||
+ | // Initialisation des champs ''statiques'' qui s'effectue en | ||
+ | // dehors de la classe. | ||
+ | |||
+ | RandomGenerator::m_seed; | ||
+ | RandomGenerator::m_generator(m_seed()); | ||
+ | </code> | ||
+ | |||
+ | ne peut pas être présente dans le fichier ''.hpp'', puisque ce fichier pourrait être chargée plusieurs fois de suite et dans ce cas, il existerait plusieurs entrées correspond à ''RandomGenerator::m_seed;''. Pour garantir qu'il ne soit généré qu'une seule entrée, il faut donc déclarer les champs dans le fichier ''.cpp'' associé au fichier ''.hpp'' pour garantir l'unicité de la déclaration. | ||
+ | |||
+ | ==== Autre syntaxe d'initialisation des champs statiques ==== | ||
+ | |||
+ | Il est désormais possible de déclarer le champ statique comme étant ''inline''. Par exemple : | ||
+ | |||
+ | <code cpp> | ||
+ | class Field | ||
+ | { | ||
+ | public: | ||
+ | inline static std::string message = "A Message"; // A partir de C++ 17 | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | est équivalent à : | ||
+ | |||
+ | <code cpp> | ||
+ | class Field | ||
+ | { | ||
+ | public: | ||
+ | static std::string message; | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | et soit dans le même fichier si la classe est déclarée dans un fichier ''.cpp'', soit dans le fichier ''.cpp'' associé au fichier ''.hpp'' dans lequel la classe est déclarée, vous ajoutez : | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | std::string Field::message = "A message"; | ||
+ | |||
+ | </code> | ||
+ | </code> | ||
+ | |||
+ | Par contre, cette nouvelle syntaxe impose que le type du champ est défini avant la définition de la classe. Si ce n'est pas le cas et que le type est simplement annoncé mais non complètement défini, il sera nécessaire de procéder selon la méthode habituelle. | ||
+ | ==== Les champs statiques désignant des constantes ==== | ||
+ | |||
+ | Dans ce cas, plusieurs syntaxes d'initialisation sont supportées pour des champs statiques désignant des constantes : | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | class Integers | ||
+ | { | ||
+ | public: | ||
+ | const static int m_one = 1; | ||
+ | const static int m_two{2}; // à partir de C++11 | ||
+ | const static int m_three; | ||
+ | }; | ||
+ | const int Integers::m_three = 3; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== Le problème de l'initialisation des champs statiques ==== | ||
+ | |||
+ | L'initialisation des champs statiques est effectué unité de compilation par unité de compilation. Ceci signifie que tous les champs statiques présents dans une unité de compilation est effectivement initialisé avant d'être utilisé. Cependant, il se peut que si un code fait appel à un champ statique d'une classe défini dans une autre unité de compilation et que ce code est appelé avant, dans ce cas, le champ ne sera pas initialisé et donc une erreur se produira au moment du chargement. | ||
+ | |||
+ | |||
+ | |||