Auteur Sujet: Tableau d'objet  (Lu 19599 fois)

jac.fil

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Tableau d'objet
« le: mai 28, 2018, 07:19:52 pm »
Question de débutant en Arduino ;)
Dans la mesure ou La référence du language sur le site Arduino ne fait aucune mention à la P.O.O, est-ce que le language C++ est totalement supporté et compilé par l'IDE?
Ma question plus précisément, peut-on créer des tableaux d'objets et dans ce cas quel est la syntaxe pour les créer ?
C'est peut-être une question triviale pour les spécialistes et je vous avoue que je n'ai pas encore lu tous les articles de la "Bible LOCODUINO", nul n'est parfait  :) :)

Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 745
    • Voir le profil
Re : Tableau d'objet
« Réponse #1 le: mai 28, 2018, 08:44:52 pm »
Bonjour
La question n'est pas aussi triviale qu'il y parait.
Le mieux est de trouver les tutoriaux sur le C++ sur le net, ils sont légions. Cela dit, le mien est pas mal :) . Le mieux est de commencer par le début :http://www.locoduino.org/spip.php?article85.
Enfin pour répondre à la question posée, l'IDE Arduino utilise le compilateur Open Source GCC qui est complet, et donc peut compiler du vrai C++ avec toutes ses subtilités et ses chausse-trappes.

jac.fil

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Tableau d'objet
« Réponse #2 le: mai 29, 2018, 08:22:16 am »
Merci pour la réponse, en effet j'ai relu les articles sur les objets sur LOCODUINO et je vais approfondir le sujet des tableaux car je vois pas comment utiliser les objets "cantons", contenant des objets "détecteurs IR",  "zone d'arrêt " et "led TCO" pour un début.
Merci aussi pour les conseils, je vais potasser le C++
À suivre ....

jac.fil

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Tableau d'objet
« Réponse #3 le: juillet 10, 2018, 01:04:04 pm »
Après 2 mois de découverte du monde de l'Arduino, et donc un peu moins débutant, je suis en mesure de répondre à ma propre question ;) ;)  peut être cela pourra t-il, servir à quelqu'un ??

Il est facilement possible ( et je m'en sert contamment) de créer un tableau d'objets selon la syntaxe suivante

Nous avons une classe Led et instancié led1, led2, led3 le tableau est déclaré ainsi

Led tableau_led []={led1,led2,led3};

Il suffit ensuite d'appeler un objet classiquement : tableau_led[2].Allume(); par exemple.

Ces tableaux sont très pratiques dans une boucle, par exemple pour allumer toutes les leds

For (i=0; i<2;i++)
     {
         tableau_led.Allume();
      }

Voilà mon petit propos est peut être évident pour les spécialistes, mais cela m'aurait été bien utile il y 2 mois car malgré mes recherches je n'avais rien trouvé et j'ai donc essayé par moi même

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 904
  • HO avec DCC++
    • Voir le profil
Re : Tableau d'objet
« Réponse #4 le: juillet 10, 2018, 03:48:53 pm »
Pas bien cherché, il y a tout sur locoduino.org,

Ces tableaux qui peuvent nous simplifier le développement Arduino : http://www.locoduino.org/spip.php?article227

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1691
    • Voir le profil
Re : Tableau d'objet
« Réponse #5 le: juillet 10, 2018, 04:26:31 pm »
C++ est un langage compliqué avec des pièges à tous les coins de rues  :)

Et donc ta question initiale était un peu trop générale pour une réponse courte, ce qui explique le silence relatif.

Oui on peut faire un tableau d'objets. Oui les objets seront construits mais le constructeur appelé sera celui sans argument : on ne peut pas passer des argument lors de la construction de chaque objet d'un tableau (en fait si, dans le cas où le tableau est initialisé explicitement, voir http://forum.locoduino.org/index.php?topic=549.msg6577#msg6577).

Dans la manière dont tu t'y es pris : déclarer des objets puis les utiliser comme valeur d'initialisation de ton tableau, il y a 2 problèmes :
  • l'initialisation du tableau réalise une copie des objets
  • La copie d'objet est piègeuse

Pour le 1., la conséquence est que tu vas avoir tes objets en doublon. D'une part tes objets led1, led2 et led3 et d'autre part une copie dans le tableau. Donc une consommation de mémoire double et un risque de confusion : appeler une méthode de led1 et une méthode de tableau_led[0] ne s'adresse pas au même objet.

Pour le 2., copier un objet est fait selon différentes façons :
  • Il n'existe pas de constructeur par recopie. La copie est réalisée en copiant brutalement la zone mémoire occupée par l'objet
  • Il existe un constructeur par recopie : un constructeur de la forme Led(Led & objToCopy) ou bien un opérateur d'affectation : operator=(Led & objToCopy). La copie est réalisée en appelant le constructeur par recopie ou l'opérateur =

Qu'est ce que ça change ? Eh bien si l'objet à copier contient un membre qui est un pointeur vers un tableau alloué dynamiquement lors de la construction de l'objet, la copie brutale réalisera une copie du pointeur. Par conséquent, l'objet et ça copie partageront ce tableau alloué dynamiquement, ce qui n'est peut être pas ce que l'on veut. Dans ce cas, il faut définir un constructeur par recopie dont le rôle sera d'allouer un tableau et d'effectuer la copie du tableau. L'objet et sa copie ont donc leur propre tableau au lieu d'en partager un seul.

En résumé, faire :

Led led1;
Led led2;
Led led3;

Led tabl[] = { led1, led2, led3 };

Ne fait sans doute pas ce que tu voudrait. Si il est nécessaire d'avoir des arguments pour construire un objet, il vaut mieux avoir un objet Led avec une méthode permettant de l'initialiser à postériori, appelons la init. Donc il suffit d'écrire

Led tabl[3];

void setup()
{
  for (int i = 0; i < 3; i++) {
    tab[i].init(monArgumentQuiVaBien);
  }
}
« Modifié: juillet 14, 2018, 09:54:12 am par Jean-Luc »
Cordialement

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Re : Tableau d'objet
« Réponse #6 le: juillet 10, 2018, 04:44:02 pm »
Bonjour

Il faut se méfier des manipulations avec des objets, le programme suivant est une adaptation du programme, présenté ci-dessus, manipulant des leds :

#include <stdio.h>
#include <iostream>

class Led {
public:
int n;
Led(int x) { n=x; }
};

Led l1(1),l2(2),l3(3);
Led ls[]={l1,l2,l3};

int main(int argc, char **argv) { int i;
for (i=0; i<3; i++) {
printf("%d ",ls[i].n); //std::cout<<ls[i].n<<" ";
}
printf("\n"); //std::cout<<<<l2.n"\n";
l2.n=0;
printf("%d\n",l2.n); //std::cout<<"\n";
for (i=0; i<3; i++) {
printf("%d ",ls[i].n); //std::cout<<ls[i].n<<" ";
}
printf("\n"); //std::cout<<"\n";
return 0;
}

On a une classe Led avec juste une variable entière qui est un numéro de led.
On déclare 3 leds l1,l2,l3 puis un tableau de 3 leds ls.

Dans le main :
- on affiche le tableau de leds avec une boucle
- on modifie le numéro de la led2 en le mettant à 0 et on l'affiche (pour vérification)
- on réaffiche le tableau de leds

Voila le résultat :

1 2 3
0
1 2 3

Bizarre le tableau n'a pas changé (notamment la led2)  :(

C'est normal quand le tableau est créé une copie des leds est faite, donc si on modifie la led2 sa copie elle n'est pas modifiée.
C'est très pernicieux et cela peut créer des bugs difficiles à trouver.

Pour éviter ces problèmes il vaut mieux utiliser des pointeurs sur les objets. C'est d'ailleurs la bonne façon de faire, pour profiter de l'héritage et du polymorphisme.

Amicalement

Pierre


savignyexpress

  • Invité
Re : Tableau d'objet
« Réponse #7 le: juillet 10, 2018, 04:46:56 pm »
Merci beaucoup Jean-Luc pour tes précisions quant aux pièges du C++.  :)

Ta proposition de méthode init appelée explicitement par la fonction setup me paraît bien plus sûre que des constructeurs appelés on ne sait pas quand. On peut donc ignorer le constructeur par défaut dans ce cas là.

La manière de procéder proposée par Jac.Fil fonctionnerait pour autant que le tableau contienne des pointeurs vers les objets.

Meilleures salutations.

Marc-Henri

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Re : Tableau d'objet
« Réponse #8 le: juillet 10, 2018, 04:52:30 pm »
Bonjour

Voila la version avec des pointeurs de leds :

#include <stdio.h>
#include <iostream>

class Led {
public:
int n;
Led(int x) { n=x; }
};

Led *l1=new Led(1),*l2=new Led(2),*l3=new Led(3);
Led* ls[]={l1,l2,l3};

int main(int argc, char **argv) { int i;
for (i=0; i<3; i++) {
printf("%d ",ls[i]->n); //std::cout<<ls[i]->n<<" ";
}
printf("\n"); //std::cout<<<<l2->n"\n";
l2->n=0;
printf("%d\n",l2->n); //std::cout<<"\n";
for (i=0; i<3; i++) {
printf("%d ",ls[i]->n); //std::cout<<ls[i]->n<<" ";
}
printf("\n"); //std::cout<<"\n";
return 0;
}

et le résultat :

1 2 3
0
1 0 3

Le numéro de la led2 à bien été modifié aussi dans le tableau de leds.

Pierre
« Modifié: juillet 10, 2018, 04:57:00 pm par Pierre59 »

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Re : Tableau d'objet
« Réponse #9 le: juillet 10, 2018, 04:55:00 pm »
Désolé,mes messages se sont un peu télescopés avec celui de Jean-Luc.

Pierre

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1691
    • Voir le profil
Re : Tableau d'objet
« Réponse #10 le: juillet 10, 2018, 04:58:10 pm »
Pas de soucis Pierre  ;)
Cordialement

Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 745
    • Voir le profil
Re : Re : Tableau d'objet
« Réponse #11 le: juillet 10, 2018, 05:35:27 pm »
Ta proposition de méthode init appelée explicitement par la fonction setup me paraît bien plus sûre que des constructeurs appelés on ne sait pas quand. On peut donc ignorer le constructeur par défaut dans ce cas là.

Je rebondis maintenant que mon internet est revenu...
Ignorer le constructeur par défaut n'est juste pas possible. Il est forcément appelé au moment de l'initialisation, et il est toujours présent ! Soit il est fourni par le programmeur, soit le compilateur en fourni un d'office qui ne fait rien. Quelle que soit la classe, je fais toujours un constructeur dit 'par défaut' qui va initialiser toutes les données membres de la classe à des valeurs bateau, généralement 0 ou NULL selon le type. Cela n'empêche nullement la solution de la fonction init, c'est juste un complément qui assure que les variables dans l'instance de la classe, les données membres ont une valeur connue et déterminée. Si on ne fait rien pour les initialiser, le contenu de chaque variable sera ce que la mémoire contient à leur emplacement au moment de la création de l'objet... ET si on oublie le init() ou que l'on fait quelque chose avant, on est pas du tout sur de ce qui va se passer. Pire, ce ne sera pas reproductible : un jour ça marche, le lendemain ça ne marche plus !

Donc la classe Led de Pierre deviendrait
class Led {
public:
int n;

Led() { n=0; }  // constructeur par défaut.
Led(int x) { n=x; }
};

Led MaLed;  // la donnée membre 'n' de MaLed est initialisée à 0. C'est probablement faux, mais c'est toujours faux de la même manière !

Pierre59

  • Sr. Member
  • ****
  • Messages: 321
    • Voir le profil
Re : Tableau d'objet
« Réponse #12 le: juillet 10, 2018, 05:56:46 pm »
Bonjour

Il me semble que le constructeur pas défaut n'est pas ajouté par le compilateur si un autre constructeur est fourni. Chaque constructeur fourni doit alors initialiser les variables.

Pierre

Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 745
    • Voir le profil
Re : Tableau d'objet
« Réponse #13 le: juillet 10, 2018, 06:51:53 pm »
Après vérification, tu as raison. Le constructeur par défaut n'est créé automatiquement par le langage que si aucun autre n'existe !

savignyexpress

  • Invité
Re : Tableau d'objet
« Réponse #14 le: juillet 11, 2018, 06:27:29 am »
Merci à tous pour vos précisions relatives aux constructeurs, un sujet que je ne connais pas bien.

Belle journée et meilleures salutations.

Marc-Henri