RESOLU Accès d'une propriété statique d'une classe par une autre classe

Bonjour,

Je suis en train de jouer avec une classe définissant des boutons radio. Tous les boutons ont en commun une propriété variable indiquant quel est le bouton actif. Cette propriété est donc définie static. Jusque la pas de problèmes, cela fonctionne à merveille.

Maintenant, je voudrai définir une deuxième série de boutons, qui ne doivent pas partager la même propriété. J'ai donc pour ne pas avoir à réécrire tout le code, dérivé une nouvelle classe. Mon problème est que cette nouvelle classe accède non pas à sa propre propriété, mais à la propriété de la classe mère.

Voici mon code de test pour cette fonctionnalité:

class Mere
{
 public: 
  static uint8_t variable; // Unique partagée entre toutes les instances
  virtual void affecte(uint8_t donnee); // Minimalisé ici, sinon grosse méthode
};
static uint8_t Mere::variable;
void Mere::affecte(uint8_t donnee) { variable=donnee; }



class Fille: public Mere
{
 public:
  static uint8_t variable; // Une variable pour Mere, une pour Fille
};
static uint8_t Fille::variable;


  
void setup()
{
  Mere mere;
  Fille fille;
  
  Serial.begin(9600);
  
  mere.affecte(10);
  Serial.println("Mere: "+String(Mere::variable)+"  Fille: "+String(Fille::variable)); // Mere: 10  Fille: 0
  // Comportement voulu, mere utilise sa variable de classe

  fille.affecte(12);              
  Serial.println("Mere: "+String(Mere::variable)+"  Fille: "+String(Fille::variable)); // Mere: 12  Fille: 0
  // Manque de pot, la méthode est chez Mere et elle travaille avec la variable de classe de Mere
}

void loop()
{
}

Ce que je cherche, c'est que fille.affecte(12); utilise Fille::variable;. Mais comme j'hérite de Mere::affecte();, cette méthode appelle Mere::variable;. Le lien vers cette propriété est défini à la compilation.

Dans mon programme, Mere hérite de tant de choses que seul variable est rajoutée et affecte() redéfinie. Si je dois redéfinir Fille::affecte(); cela est équivalent à couper coller Mere et à changer les Mere en MereBis (Mere2, c'est mon code... pardon pour la grossièreté).

Y a-t-il moyen que affecte() accède à la bonne classe sans la redéfinir?

Merci

Une fonction virtuelle existe dans Mere mais pas dans Fille.

class Fille: public Mere
{
 public:
  static uint8_t variable; // Une variable pour Mere, une pour Fille
  virtual void affecte(uint8_t donnee); // Minimalisé ici, sinon grosse méthode
};
static uint8_t Fille::variable;
void Fille::affecte(uint8_t donnee) { variable=donnee; }

Effectivement, mais si je mets une fonction affecte() dans Fille:

-Si elle fait appel à Mere::affecte() cela ne change rien, car Mere::affecte() appelle toujours Mere::variable.

-Si je affecte variable dans Fille::affecte(), évidemment cela fonctionne, mais j'ai tout dupliqué. Autant copier/coller pour écrire MereBis et MerTer (pas mer/terre!), sachant que si je ne les utilise pas, tout sera viré au moment de l'édition des liens.

-Si je affecte variable dans Fille::affecte(), évidemment cela fonctionne, mais j'ai tout dupliqué.

Oui mais je ne pense pas que tu puisse faire autrement.

Autant copier/coller pour écrire MereBis et MerTer (pas mer/terre!), sachant que si je ne les utilise pas, tout sera viré au moment de l'édition des liens.

Pas tout à fait. Je suppose que si Fille hérite de Mere c'est qu'il y a un intérêt à le faire, et qu'il y a d'autres variables d'instance et d'autres méthodes non statiques, sinon l'héritage n'aurait pas d'intérêt, et le polymorphisme non plus.

Hello

J'ajoute mon grain de sel :slight_smile:

Perso, lorsque je suis confronté à une difficulté de mise en oeuvre quelconque, j'essaye de revenir en arrière pour questionner mes postulats de base.
Et souvent je réalise que c'est le postulat de base qui n'est pas parti dans la meilleure direction, ce qui entraîne des complications plus loin.

Ici en l'occurrence, je dirais : la difficulté provient de l'idée de gérer plusieurs choses dans une même classe, c'est-à-dire chaque bouton individuellement, ainsi qu'une notion de groupe de boutons.

Prenons l'hypothèse d'une première classe bouton toute simple, et d'une seconde classe pour gérer une liste de boutons, dont un "actif".
Sur cette hypothèse, il n'y a plus d'embrouille de propriété statique, et tes mères-filles deviennent juste deux instances de la seconde classe.

Ce qui n'est pas faux :wink:

je ne sais pas si ça répond à votre question mais voici un exemple utilisant les template qui vous permet de définir des groupes de mère et dans chaque groupe de mères, des groupes de filles.

avec une variable de classe par groupe, on peut compter automatiquement lors des instanciations (et éventuellement delete) le nombre d'enfants et les enfants par groupes.

// --- LES MERES ---
template <uint8_t groupID>
class Mere
{
  public:
    static uint8_t compte;
    Mere() : _groupID(groupID) {
      compte++;
    }
    ~Mere() {
      compte--;
    }

  private:
    uint8_t _groupID;
};

template <uint8_t groupID>
uint8_t Mere<groupID>::compte = 0; // réserve la mémoire

// --- LES FILLES ---
template <uint8_t groupID, uint8_t subgroupID>
class Fille : public Mere<groupID>
{
  public:
    static uint8_t compte;

    Fille() : _subgroupID(subgroupID) {
      compte++;
    }

    ~Fille() {
      compte--;
    }
  private:
    uint8_t _subgroupID;
};

template <uint8_t groupID, uint8_t subgroupID>
uint8_t Fille<groupID, subgroupID>::compte = 0; // réserve la mémoire

// --- LE CODE ---

// un groupe de mère '#1' qui a 5 filles dans deux groupes '#10' et '#20'. on a deux filles '#10' et trois filles '#20'
Fille<1, 10>* filleGroup10A1 = new Fille<1, 10>;
Fille<1, 10>* filleGroup10B1 = new Fille<1, 10>;
Fille<1, 20>* filleGroup20A1 = new Fille<1, 20>;
Fille<1, 20>* filleGroup20B1 = new Fille<1, 20>;
Fille<1, 20>* filleGroup20C1 = new Fille<1, 20>;

// un autre groupe de mère '#2' qui a 3 filles dans deux groupes '#10' et '#20'. on a une fille '#10' et deux filles '#20'
// allocation sans appel à new, on ne peut pas appeler delete sur ceux là
Fille<2, 10> filleGroup10A2;
Fille<2, 20> filleGroup20A2;
Fille<2, 20> filleGroup20B2;

void setup()
{
  Serial.begin(115200);
  Serial.print(F("Mere Groupe 1 a ")); Serial.print(Mere<1>::compte); Serial.println(F(" enfants")); // Mere Groupe 1 a 5 enfants
  Serial.print(F("  Fille Groupe 10: ")); Serial.println(Fille<1, 10>::compte);                     //   Fille Groupe 10: 2
  Serial.print(F("  Fille Groupe 20: ")); Serial.println(Fille<1, 20>::compte);                     //   Fille Groupe 20: 3

  Serial.println();

  Serial.print(F("Mere Groupe 2 a ")); Serial.print(Mere<2>::compte); Serial.println(F(" enfants")); // Mere Groupe 2 a 3 enfants
  Serial.print(F("  Fille Groupe 10: ")); Serial.println(Fille<2, 10>::compte);                     //   Fille Groupe 10: 1
  Serial.print(F("  Fille Groupe 20: ")); Serial.println(Fille<2, 20>::compte);                     //   Fille Groupe 20: 2

  Serial.println(F("\nDestruction d'une fille Groupe 1\n"));
  delete filleGroup10A1;
  Serial.print(F("Mere Groupe 1 a ")); Serial.print(Mere<1>::compte); Serial.println(F(" enfants")); // Mere Groupe 1 a 4 enfants
  Serial.print(F("  Fille Groupe 10: ")); Serial.println(Fille<1, 10>::compte);                     //   Fille Groupe 10: 1
  Serial.print(F("  Fille Groupe 20: ")); Serial.println(Fille<1, 20>::compte);                     //   Fille Groupe 20: 3
}

void loop() {}

la console série ouverte à 115200 bauds vous dira

[color=purple]Mere Groupe 1 a 5 enfants
  Fille Groupe 10: 2
  Fille Groupe 20: 3

Mere Groupe 2 a 3 enfants
  Fille Groupe 10: 1
  Fille Groupe 20: 2

Destruction d'une fille Groupe 1

Mere Groupe 1 a 4 enfants
  Fille Groupe 10: 1
  Fille Groupe 20: 3
[/color]

Voyons si j'ai bien compris.

Tu dois gérer un groupe de radio-boutons.
Tu as créé une classe Mere (on va plutôt dire classe Bouton) qui gère un bouton.
Tu en crées autant que de boutons, mais ça ne suffit pas : chaque instance (chaque objet Bouton) doit connaître le numéro du bouton sélectionné.
Tu as donc ajouté ce membre statique (le nom choisi est vraiment nul, désolé).
Et, OK, ça marche.

Maintenant, tu veux ajouter un second groupe de radio-boutons.
Tu vois vite que si tu instancies d'autres objets de la classe Bouton, ça ne va pas : il y a un seule valeur (statique) pour tous les objets.

Alors, pour ne pas dupliquer du code, tu prends la plus mauvaise décision possible : tu dérives la classe Bouton en une classe Bouton2 (=Fille).

Ca n'a pas de sens. Les boutons du second groupe n'ont pas un comportement différents de ceux du 1er groupe, il ont juste une donnée commune propre.

Résumons. Tu as quoi dans ton système ? 2 groupes de boutons, avec chacun plusieurs boutons.
Le rôle du groupe de boutons est de mémoriser le n° du bouton sélectionné. Rien d'autre.

class GroupeBoutons {
public:
  GroupeBouton () { num_selected = -1; }
private:
  int num_selected;
  friend class Bouton; // on aura besoin de ça
};

Ensuite, tu as des objets Boutons. Tu sais ce qu'il ont à faire, mais pour connaître le bouton actuellement sélectionné, chaque bouton doit connaître l'objet GroupeBoutons dont il dépend. On lui passe l'adresse du groupe à la construction :

class Bouton {
public:
  Bouton ( GroupeBoutons& groupe, ... ) :
  mGroupe (groupe)
{ ... }
private:
  GroupeBoutons& mGroupe;
};

Maintenant chaque bouton accède à l'info par mGroupe.num_select.

Ca me semble beaucoup plus propre que de dériver une classe juste pour ctéer une seconde instance de la classe Mère !

Autant copier/coller pour écrire MereBis et MerTer (pas mer/terre!), sachant que si je ne les utilise pas, tout sera viré au moment de l'édition des liens.

Pas tout à fait. Je suppose que si Fille hérite de Mere c'est qu'il y a un intérêt à le faire, et qu'il y a d'autres variables d'instance et d'autres méthodes non statiques, sinon l'héritage n'aurait pas d'intérêt, et le polymorphisme non plus.

En fait, j'ai une classe de grand-mères qui a des boutons... Heu, non j'ai une class GrandMere et c'est des boutons individuels. Je dérive de GrandMere la classe Mere qui est une classe de boutons radio (1 seul actif). Cette classe est parfaite, fonctionne bien pour une série de boutons. Et ça marche bien car tous les boutons partagent une même variable. Maintenant su je veux deux séries de boutons, je ne peux pas prendre la même classe à cause de la fameuse variable. Il m'en faut 2. Comme Mère est parfaite, en faisant une classe Fille identique, mais avec une nouvelle variable, cela aurait résolu le problème. Mais Fille accède pus aux affaires de sa Mere qu'aux siennes.

Perso, lorsque je suis confronté à une difficulté de mise en oeuvre quelconque, j'essaye de revenir en arrière pour questionner mes postulats de base.
Et souvent je réalise que c'est le postulat de base qui n'est pas parti dans la meilleure direction, ce qui entraîne des complications plus loin.

J'en ai bien l'impression

Ici en l'occurrence, je dirais : la difficulté provient de l'idée de gérer plusieurs choses dans une même classe, c'est-à-dire chaque bouton individuellement, ainsi qu'une notion de groupe de boutons.

Prenons l'hypothèse d'une première classe bouton toute simple, et d'une seconde classe pour gérer une liste de boutons, dont un "actif".
Sur cette hypothèse, il n'y a plus d'embrouille de propriété statique, et tes mères-filles deviennent juste deux instances de la seconde classe.

J'ai bien deux classes GrandMere boutons individuels et Mere boutons radio, mais les instances de la seconde classe sont toutes liées entre elles, comme dans ce que j'ai écrit. Ce que je cherchais c'est un moyen d'avoir deux ensembles différents de boutons radios.

Habituellement quand on fait cela, on crée la classe Mere des boutons radio, et dans les paramètres, on ajoute un attribut de groupe (Android, Windows...). Ma limitation, c'est la taille mémoire d'un Arduino, qui n'apprécie pas beaucoup les codes à rallonge. Je peux effectivement utiliser N variables soit sous forme de liste chaînées, soit sous forme de tableau, mais j'augmente pas mal la taille du code dans le cas ou je n'ai qu'une seule série de boutons (ce qui est le plus fréquent).

D'où l'idée de dupliquer la classe dans les quelques cas où j'en ai besoin. Le mieux serait de ne pas dupliquer les fonction, d'où la création d'une classe dérivée. Mais ça marche pas.

Je pense que les templates ne résolvent pas mon problème, parce qu'il y a aussi duplication du code, mais pour une fois que je vois un code clair, je vais l'analyser plus en détail. Je ne connaissait pas l’existence des templates.

Posted by: biggil Voyons si j'ai bien compris.

Je confirme et ta solution me convient. D'un point de vue beauté du code, c'est impec. En plus c'est simple!

Merci à tous

Comme souvent c'est l'analyse du système qui était trop rapide. Tu avais défini le concept de bouton, mais pas celui de groupe de boutons, dont tu as pourtant besoin (et plusieurs fois).
A partir du moment où tu commences à parler de groupes de boutons, il devient "naturel" de créer une classe pour modéliser ce truc.

Pour ma défense, j'ai eu besoin de boutons normaux, et j'avais une classe de boutons normaux. Quand j'ai eu besoin de boutons radio, j'ai créé une deuxième classe de boutons, les radios. Tout cela a bien fonctionné, je n'avais qu'une série. En fait je me suis inspiré de la programmation windows qui a une classe de boutons et une classe de boutons radio. C'est ce qui est fait aussi dans la librairie chp_vcl_02 qui s'inspire d'Androïd.

La ou j'ai fait une erreur d'analyse était que les radios que j'ai créés ne pouvaient pas fonctionner pour plusieurs séries. Et il ne fallait pas bricoler pour le faire fonctionner, il fallait que je reprenne à l'étape d'avant.

En fait la solution que tu m'as donné utilise non pas une classe, mais deux classes supplémentaires, une pour le groupe, une pour les boutons qui sont dedans. C'est d'une logique terrible quand on a la solution... Comme chaque fois que l'on trouve une erreur! Mais si créer une classe était naturel, en créer deux l'est moins.

Maintenant, comme je n'ai pas une place illimitée pour le code et surtout pour les données, je crois que je vais prendre plusieurs solutions, et je choisirais celle que je prends en fonction de mon application.

Je suis surtout conscient que dans tous les cas, si cela ne fonctionne pas, c'est forcément de ma faute!

Généralement la structure d’affichage est sous forme de fenêtres appartenant à une application qui contiennent des vues.

Une vue peut aussi contenir des sous vues de maniere récursive, ce qui permet les groupements

La gestion des événements clavier, souris, appui etc se fait au niveau du conteneur le plus haut, généralement l’application qui regarde quelle est la vue visible qui est le plus haut dans la pile sous cet événement et lui envoie l’événement. La vue répond si elle prend la main (devient donc l’element actif) ou alors elle passe l’évènement à sa vue englobante jusqu’à trouver une view qui accepte cet événement. Si personne ne veut cet événement il est simplement ignoré.

Quand un événement n’est plus destiné à l’élément actif généralement l’application appelle d’abord une méthode de la vue active pour lui demander si elle veut abandonner son rôle de vue active, si oui elle se met en ordre et le contrôle passe à la nouvelle vue.

Les boutons, champs de texte, etc sont alors tous des sous classes de cette classe vue.

Ancien amateur ayant programmé sous window, c'était ma démarche première jusqu'à ce jour.

Je joue avec un écran tactile VMA412 320x240, enfichable directement sans fils sur un UNO

J'ai une classe Zone initiale capable de recevoir des zones voisines et des sous-zones, de transmettre des évènements touchpad (onClic) et de transmettre des ordres de se dessiner (draw). De cette classe de base dérivent un Bouton (en fait quasi une zone), un BoutonRadio, et un Ecran.
On insère dans Ecran des Zone, des Bouton, des BoutonRadio, dans chacun de ces objets, on peut aussi insérer les mêmes objets.

C'est inspiré de la programmation sur ordi, Mais si sur un ordi, je peux programmer des logiciels aussi gros que me permet ma patience, sur l'Arduino, je suis limité à la taille de la Flash et de la mémoire vive.

Pour un objet quelconque, il me fait au moins:

  • la zone d'action 4 nombres pouvant aller de 0 à 319 -> 8 octets (projet éventuel de mette les bits de poids forts dans un octet à part, ce qui réduit à 5 octets, mais augmente le code et le temps de traitement. Autre solution: ignorer le bit de poids faible, ce qui réduit à 4 octets)
  • les deux pointeurs sur les voisins et ceux qui sont en dessous -> 4 octets
  • deux pointeurs sur des fonctions utiles externes(draw et onClic) -> 4octets
    sans compter des variables utiles (sélection ou pas, couleur, forme...)
    Soit 16 octets.

Avec une mémoire de 2ko pour un UNO, 500 octets pour la phrase "#include <SD.h>", il ne me reste avant plantage que 1500 octets si je ne mets que des boutons, soit pas plus d'une centaine. En fait j'arrive à ne mettre que 43 boutons radio, si je n'ai rien d'autre dans mon programme (que faire de 43 boutons si ils ne servent à rien? J'irais poser la question à un ado, il ont une science immense sur les boutons).

Je joue actuellement avec paint.ino fortement inspiré de paint.exe de XP:

Heu... il semblerait qu'il y ait 42 boutons. En fait pas vraiment, les 28 cases du choix de la couleur sont considérées comme une seul bouton (même fonction). Il n'y a que 16 boutons.

Mais je me rends compte que même avec ce programme (qui occupe 95% de ma mémoire de mon UNO), je n'ai pas besoin de sous-zones, par contre, j'ai presque un problème de taille de données et de code.
Mais j'ai quand même 2 ensembles de boutons radio (crayon, gomme, cercles.. pour l'un; couleur fond/forme pour l'autre).

En conséquence pour un ordinateur zones et sous-zones, c'est pas mal (double liste pour les contrôles)
Pour Arduino,je vais me limiter à une simple liste sans sous-zones, sans onglets.

Autre chose, actuellement avec des zones et des sous-zones, cela ressemble à:
Bouton unBouton;
ecran.insere(unBouton);
Alors que sans zone, l'insertion peut être masquée dans le constructeur (elle se fait obligatoirement dans l'écran):
Bouton unBouton;
suffit. C'est quand même plus simple. Mais on es limité, bien sûr.
Si ma bibliothèque de base est utilisée par quelqu'un d'autre, c'est plus simple à comprendre