Bonjour tout le monde,
Dans un but didactique je fais un portage d'un de mes sketches Processing en openFramekorks...
Je dois dire que ça pique un peu ! J'ai des vieux relents de mes années d'études d'informatique ou j'ai pourtant mangé du code C/C++ et assembleur jusqu'à l'indigestion. Mais la je dois dire qu'il me reste plus grand chose de mes notions de pointeurs. Je vois bien comment cela fonctionne, les histoire d'adresses, de valeurs et de références... mais impossible de trouver une solution pour recréer le tableau que j'utilise dans Processing.
Je veux créer une forme avec un ensemble de billes, donc en gros un tableau de 80 billes.
dans Processing je faisais comme suit :
- créer la classe Bille
- j'instancie une forme qui est un ensemble de 80 billes :
Bille[] maForme = new Bille[80];
- j'initialise les éléments de maForme (avec paramètre n dans mon constructeur) :
for (int i = 0; i < 80; i++) { maForme[i] = new Bille(n); }
Sous openFrameworks je tente un truc du genre (sans succès) :
- Je crée une classe Bille (avec fichier .cpp et .h).
- j'instancie ma forme dans la classe testApp :
Bille* maForme[64];
- j'initialise les éléments de maForme dans testApp::setup() :
for(int i = 0; i < 80; i++) { maForme[i] = new Bille(n); }
Forme que j'affiche dans testApp::draw() :
maForme->draw();
J'obtiens l'erreur (incompréhensible pour moi) :
si quelqu'un peu m'éclairer...
Dernière modification par Marty (2013-03-18 17:46:48)
Hors ligne
Bon je me répond,
Evidement il y avait une petite (grosse !) faute au niveau de la méthode d'affichage, dans testApp::draw() il faut évidement remplacer
maForme->draw();
qui n'a pas de sens par :
for(int i = 0; i < 80; i++) { maForme[i]->draw(); }
Maintenant il y a une subtilité que je ne comprend toujours pas. Dans un exemple sur le net ils utilisent quelque chose qui ressemble à ce que je fais mais :
- ils créent la classe Balle
- ils instancient un tableau de 100 Balles :
Balle tousMesBallons[100];
- ils affichent tous les ballons :
for(int i=0; i<100; i++) { tousMesBallons[i].draw(); }
Je remarque dans ce cas qu'on ne passe pas par la phase de création d'un objet par opérateur "new".
Quelle différence avec ma solution ?
Hors ligne
Bonsoir,
Apparemment quand on déclare un array, il est initialisé en utilisant le constructeur de la classe:
http://msdn.microsoft.com/en-us/library … s.80).aspx
Dernière modification par lilive (2013-03-18 20:30:43)
Hors ligne
Bonsoir,
Comment alors appeler le constructeur en lui passant des paramètres pour initialiser des variables de la classe ?
par exemple dans ma solution si je veux initialiser les coordonnées de chaque bille je peux faire :
for (int i = 0; i < 80; i++) { maForme[i] = new Bille(x,y); }
Dans le cas de l'exemple cité je suis obligé de passer par une méthode spécifique ou gérer chaque variable indépendament ? du genre :
for (int i = 0; i < 80; i++) { maForme[i].x = x; maForme[i].y = y; }
Mais du coup pour moi ces deux solutions sont exactement les mêmes (mais je suis encore débutant). Pourquoi utiliser l'une plutôt que l'autre ?
PS : d'ailleurs je me rappelle que dans Processing il était obligatoire d'initialiser les variables du tableau avec new, sinon il sortait une erreur. Mais c'est peut être spécifique à Processing.
Hors ligne
Oui ça se ressemble fort.
Un avantage avec
Bille maForme[64];
c'est que tu ne te retrouves pas avec un tableau de pointeurs pointant sur rien avant la boucle de création des objets, ça fait une petite source d'erreur en moins, mais ce n'est pas énorme.
Il y a peut-être d'autres avantages et inconvénients que je ne vois pas, étant moi-même débutant en C++
Hors ligne
Hmmmm quand tu fais : Bille* maForme[64];
c'est un tableau de pointeur… et plus généralement comme des tableaux sont des pointeurs, tu fais un pointeur de pointeur…
d'où tu passes vers new bille
Balle tousMesBallons[100];
c'est juste un tableau de 100 balles.
Comme tu l'as compris dans tes expérimentations, ce sont des choses très différentes que tu demandes à ton ordi
Hors ligne
Ok merci pour les infos.
Oui j'ai mis
Bille* maForme[80]
parce que je cherchais à utiliser l'opérateur "new" par analogie avec Processing, et comme new renvoie un pointeur je passe par un pointeur de pointeur mais je ne l'ai pas fait avec une idée précise dans la tête. Je ne me suis pas demandé : "oui j'ai besoin de passer par les pointeurs"
Quelle utilité alors d'avoir un constructeur dans le deuxième cas si on ne peux l'utiliser que sans paramètres ? il n'y a pas non de surcharge du constructeur possible dans ce cas ?
C'est frustrant de ne pas y voir plus clair !
Si un kador du C++ passe par ici je serais ravi d'en savoir plus !
Dernière modification par Marty (2013-03-18 21:47:21)
Hors ligne
L'utilité est d'initialiser ton objet.
Certes, tu ne peux peut etre pas lui transmettre des paramètres, mais ça ne veut pas dire qu'il ne transmet rien.
Hors ligne
Certes...
Mais on est d'accord qu'on ne peux du coup pas surcharger le constructeur dans ce cas ? Je ne peux pas avoir deux type de constructeur comme :
Bille(x,y)
et
Bille(x,y,couleur)
Ca reste très flou...
Hors ligne
Non tu ne peux pas le surcharger dans ce cas là, à moins que tu changes le code.
Mais c'est rien de faire un : type bille (type b, type a){ bla bla;}
etc d'en faire dans tous les cas qui te semblent important
Hors ligne
Marty a écrit:
Quelle utilité alors d'avoir un constructeur dans le deuxième cas si on ne peux l'utiliser que sans paramètres ? il n'y a pas non de surcharge du constructeur possible dans ce cas ?
Hein ?
En fait je ne vois pas trop ce qui te pose problème.
Si tu veux créer tes objets en passant des paramètres au constructeur, tu peux faire comme tu as fait.
On peut aussi passer des paramètres au constructeur en faisant comme dans le lien que j'ai donné, mais dans ton cas ça ne va pas être bien commode !
Ou alors comme tu disais, tu fais dans une boucle des
Si je ne dis pas de bétise, en mémoire l'organisation des choses sera différente.
Bille maForme[64]
Le tableau contiendra à la suite les blocs d'octets de chaque objet. Sa taille sera sizeof(uneBille)*64.
Bille * maForme[64]
Le tableau contiendra des pointeurs vers des Billes, sa taille sera 64 fois la taille d'un pointeur, donc bien plus petit si Bille a de nombreuses propriétés. Et ailleurs dans la mémoire il y aura 64 blocs de sizeof(uneBille) octets.
On peut penser que l'accès à une bille du tableau sera plus rapide en évitant le tableau de pointeur, mais je ne crois pas que ce soit un critère très valable.
Dernière modification par lilive (2013-03-18 22:10:35)
Hors ligne
lilive a écrit:
En fait je ne vois pas trop ce qui te pose problème.
Si tu veux créer tes objets en passant des paramètres au constructeur, tu peux faire comme tu as fait.
C'était le sens de mon interrogation, entre autre. A savoir : si je veux (ou j'ai besoin) de surcharger le constructeur pour une raison ou une autre, je dois passer par les pointeurs ? Et pourquoi ca marche dans un cas et pas dans l'autre ?
Et là ou il y a "problème" c'est que ça fonctionne mais je ne sais toujours pas ce qui fait que l'utilisation des pointeurs est nécessaire dans ce cas. Je suis peut être perfectionniste, mais c'est comme si on te donne un problème et qu'on te dit : la réponse c'est x = 3. Cool ! mais c'est frustrant de ne pas comprendre comment on en est arrivé la !
Dernière modification par Marty (2013-03-18 22:27:20)
Hors ligne
Le cas que tu présentes n'est pas assez précis pour savoir pourquoi il était nécessaire d'utiliser des pointeurs.
Hors ligne
En accord avec le lien donné plus haut:
Bille billes[10];
crée un tableau de 10 Bille, les objets sont créés avec le constructeur sans paramètres Bille(), qui doit exister.
Bille billes[10] = { Bille(20, 30), Bille(1, 2, 0.5f) }
crée un tableau de 10 Bille, les objets sont créés avec le constructeur sans paramètres Bille(), sauf le premier qui est créé avec un constructeur disons Bille(int, int) et le deuxième qui est créé avec un constructeur disons Bille(int, int, float)
Bille billes[2] = { Bille(20, 30), Bille(1, 2) }
crée un tableau de 2 Bille, les objets sont créés avec un constructeur disons Bille(int, int). Le constructeur sans paramètre Bille() n'est pas obligé d'exister.
Si cela ne correspond pas à tes besoins:
Bille * billes[10];
crée un tableau de pointeurs vers des objets Bille. Il faut remplir le tableau à la main.
billes[0] = new Bille(); billes[1] = new Bille(10, 20); billes[2] = new Bille(1, 2, 0.5f);
Les 3 premiers éléments du tableau sont affectés de pointeurs vers 3 objets Bille, chacun instancié par un constructeur différent.
Est-ce qu'il reste quelque chose que tu ne comprends pas ?
Hors ligne
Bonjour,
Merci pour toutes ces infos, ça devient un peu plus clair.
En tout cas je viens de comprendre que les deux solutions sont valables et équivalentes à ceci près que l'opérateur "new" permet une allocation dynamique de la mémoire. La notion n'est pas encore parfaitement claire dans mon esprit mais je vois un peu mieux la différence, en tout cas dans le choix de son utilisation.
Ceci dit dans mon cas par exemple, si je veux utiliser
Bille maForme[80];
c'est quand même beaucoup plus contraignant d'initialiser toutes les billes qu'avec une allocation dynamique non ?
Par exemple j'ai un constructeur
Bille(x, y, z, couleur, visible)
Je veux avoir des valeurs aléatoires pour les coordonnées, j'ai donc dans le cas de l'allocation dynamique une boucle :
for (int i =0; i<80; i++) { maForme[i] = new Bille(ofRandom(0, ofGetWidth()),ofRandom(0, ofGetHeight()),ofRandom(-150, 150),155,true); }
Le faire sans allocation dynamique me parait beacoup plus contraignant !
Dans mes recherches je suis d'ailleurs tombé sur un document très clair et simple qui explique les concepts du C++ et de la programmation orientée objet. Je vous donne le lien si ça peut intéresser :
http://www.lpmm.univ-metz.fr/collard/do … C++.v1.pdf
Tout est clairement expliqué, pas toujours très complet mais très accessible.
Ca finira par rentrer dans le crâne
Merci à vous tous.
Dernière modification par Marty (2013-03-19 11:51:10)
Hors ligne