Bonjour,
Quelles solutions existe-t-il pour donner à une classe l'accès aux variables du programme principal, où elles sont instanciées?
Merci d'avance
Bonjour,
Quelles solutions existe-t-il pour donner à une classe l'accès aux variables du programme principal, où elles sont instanciées?
Merci d'avance
Bonjour
Si une classe a besoin d'une variable, il faut lui donner en paramètre (au constructeur, ou lors de l'appel à une méthode).
Soit ton explication est bancale, soit tu as un souci de conception.
Peux-tu détailler plus ton souci (en donnant un bout de code par exemple) ?
Oui, merci.
En fait, mon programme principal contient deux variables qui récupèrent lors de leur initialisation la taille de l'écran: "double width, height;"
Et il y a une classe "Affichage" qui utilise la bibliothèque SFML. Une de ses fonctions-membres produit des sprites, mais doit les redimensionner selon la taille de l'écran. Elle doit donc avoir accès à "width" et "height".
J'ai pensé à les passer en argument, mais je n'aime pas ça parce que mon programme nécessite le passage répété de dizaines de variables. Ca pose surtout le problème du retour, puisque les méthodes ne peuvent retourner qu'une variable à la fois. Donc, si je veux faire circuler mes valeurs comme ça, je suis obligé d'appeler des "getters" sans arrêt, et je pense que si ça a l'air nul, c'est que ça doit l'être.
Il y a surtout une instance de la classe "RenderWindow" (SFML), déclarée dans le programme principal, que je ne peux pas passer, et qui m'empêche d'appeler la fonction "draw" depuis les autres classes.
Une classe est une représentation d'un concept, et de son comportement interne.
L'application est un niveau de modélisation supérieur.
Si la classe a besoin de quelque chose pour son fonctionnement interne, elle le demande. Par des arguments, comme l'a dit Ehonn.
Cela laisse à l'utilisateur de la classe le choix de ce qu'elle doit lui donner, que cet utilisateur soit une autre classe ou l'application.
La classe est garante de son intégrité, et propose deux types de fonctions:
- l'apparence via des lecteurs de propriétés (je n'ai pas dit variables membres)
- les comportements: constructeurs, destructeur, fonctions non-const
De mon point de vue (personnel, j'insiste), les seules fonctions membres statiques sont protégées ou privées.
Le comportement externe d'une classe (telle que le fait de pouvoir faire une somme de deux nombres complexes) relève des fonctions libres.
J'entends par externe toute action dans laquelle l'objet n'est pas clairement l'acteur.
edit: zut doublé.
Mes principes de bases du codeur qui veut pouvoir dormir:Pour faire des graphes, essayez yEd.
- Une variable de moins est une source d'erreur en moins.
- Un pointeur de moins est une montagne d'erreurs en moins.
- Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
- jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
- La plus sotte des questions est celle qu'on ne pose pas.
le ter nel est le titre porté par un de mes personnages de jeu de rôle
Attention! Je ne donne pas aucun argument à mes classes. Simplement, je leur donne les arguments nécessaires pour qu'elles puissent travailler.
Par-exemple, une de mes classes fait les monstres de mon jeu. Disons des lézards ou des serpents.
Si je veux un serpent, l'argument sera 1, et la classe chargera les textures qui correspondent à ce numéro.
Mais je ne peux pas passer un "RenderWindow", et je voudrais que la fonction "draw" puisse être appelée depuis l'intérieur de la classe. Tu vois?
Tu devrais avoir un programme indépendant de son affichage.
Une classe unique devrait servir de connecteur à SFML
J'estime que les #include liés à toute bibliothèque graphique devrait être rejeté dans un fichier d'implémentation unique.
ici, dans le fichier d'implémentation de Affichage.
Cette seule classe devrait avoir à gérer la taille de l'écran.
Pour qu'on aie une idée de l'ensemble, montre nous ton main, et dis-nous quelles classes interviendraient si ton programme se résumait à un unique monstre, qui se déplace aléatoirement dans un pré (un terrain limité).
Tu pourrais avoir un gestionnaire de texture.
Ainsi, affichage.draw(Monstre) serait à peu près:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 void Affichage::draw(Monstre const& m, Camera const& camera) { pixel origine = camera.asPixel(m);//la caméra permet la conversion de coordonnées en jeu vers des pixels à l'écran //je ne connais pas (encore) sfml, mais il doit bien y avoir un drawAt(x, y); textures.get( m.getTexture() ).drawAt(origine.getX(), origine.getY()); }
Mes principes de bases du codeur qui veut pouvoir dormir:Pour faire des graphes, essayez yEd.
- Une variable de moins est une source d'erreur en moins.
- Un pointeur de moins est une montagne d'erreurs en moins.
- Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
- jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
- La plus sotte des questions est celle qu'on ne pose pas.
le ter nel est le titre porté par un de mes personnages de jeu de rôle
Pour l'affichage, tes objets peuvent avoir une méthode draw qui prend une sf::RenderWindow & et qui affiche le srpite de l'objet.
Le "souci" c'est que le code va ressembler à ceci :
Alors qu'avec les sprites de la SFML, ça sera :
Code : Sélectionner tout - Visualiser dans une fenêtre à part ton_objet.draw(window);
Pour resoudre ceci, la SFML propose d'hériter de sf::Drawable : sf::Drawable - Detailed Description.
Code : Sélectionner tout - Visualiser dans une fenêtre à part window.draw(un_sprite);
Personnellement, j'ai préférer écrire l'opérateur << entre une sf::RenderWindow & et mes objets pour écrire :
Code : Sélectionner tout - Visualiser dans une fenêtre à part window << mon_objet;
Je peux pas te montrer le main() comme ça, il fait un kilomètre de long!
Avec SFML je ne sais pas s'il y a un drawAt(), mais il y a draw() qui récupère la position X et Y de l'objet en question pour l'afficher au bon endroit.
Mais la question c'est pas ça. je t'explique mieux:
Le flux d'affichage est créé dans "main()", et la fonction "draw()" doit être appelée depuis les classes. Mais le compilateur n'est pas d'accord parce que le flux d'affichage n'est pas déclaré dans les classes. C'est ça que je dois résoudre.
Oui et non, tu peux renvoyer des objets qui ont plusieurs attributs. Regarde du côté de std::pair et std::tuple. Dans ton cas, sf::Vector2 aurait pu convenir. Il ne faut surtout pas hésiter à créer sa propre classe afin d'avoir des noms d’attributs ou de méthodes plus adaptés à son projet.
Poste un exemple minimal car on a du mal à voir comment tu as découpé les choses. Les draw doivent se faire entre window.clear(); et window.display();, c'est entre ces deux lignes que tu dois connaître les objets que tu veux afficher.
J'oserai dire qu'un main trop long pour le montrer est mauvais signe.
Tu devrais avoir des déclarations de variables "pas si globales que ca", et une boucle classique événements/dessin.
L'héritage de sf::drawable est intéressant.
Je trouve intéressant l'usage de operator<<
tu peux même aller encore plus loin en définissant des manipulateurs (à la iomanip, tels que std::endl) pour window.clear() et window.display().
ce qui pourrait donner:
ou dans sa version tout objet.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 while(running()) { processEvents(); window << clear << background; for (Drawable & d : drawables) { window << d; } window << ui << draw; }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 while(running()) { ui.processEvents(game); window << clear << game << ui << draw; }
Mes principes de bases du codeur qui veut pouvoir dormir:Pour faire des graphes, essayez yEd.
- Une variable de moins est une source d'erreur en moins.
- Un pointeur de moins est une montagne d'erreurs en moins.
- Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
- jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
- La plus sotte des questions est celle qu'on ne pose pas.
le ter nel est le titre porté par un de mes personnages de jeu de rôle
Voici un extrait. J'ai enlevé plein de trucs, et même complet il n'est pas encore tout-à-fait fonctionnel. C'est une réécriture (en-cours) d'une version qui marchait, mais trop mal)
Autre détail: tout ça est écrit dans main(), parce qu'avant j'avais un main() vide qui ne faisait qu'appeler la fonction principale. J'ai économisé un appel =D
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 #include <SFML/Graphics.hpp> #include <iostream> #include <windows.h> #include <string.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <ctime> #include "Commande.h" #include "Sauvegarde.h" using namespace std; using namespace sf; int main() // Programme principal { int width(GetSystemMetrics(SM_CXSCREEN)); // Récupérer résolution horizontale de l'écran int height(GetSystemMetrics(SM_CYSCREEN)); // Récupérer résolution verticale de l'écran // Variable pour stocker à très court terme le résultat d'un calcul int calc2; // Gestion du temps temps clock_t time1(clock()), time2; // Temps int step(0); // Cycles écoulés int cont(0); // Compteur de cycles class Texte4 // Afficher le menu Texte4 { private: double width, height, heightText; // Variables contenant les dimensions de l'écran et du texte int options, selection; // Gestion des options et de la sélection int zTexte, iTexte; // Index d'animation Text texte1, texte2, texte3; // Objets "texte" Font fonte; // Fonte pour le texte Color couleur; // Couleur pour le texte double x1, x2, x3; // Coordonnées X double y1, y2; // Coordonnées Y double calc1, calc2; // Variables de travail double X, j; // Valeurs de modification, pour l'animation bool retourTexte; // Témoin d'animation public: Texte1(double a, double b, Font c, Color d) // Constructeur { // Récupération des arguments width=a; height=b; fonte=c; couleur=d; heightText=height/13; // Taille du texte pour les lignes principales // Attribution de la fonte au texte texte1.setFont(fonte); texte2.setFont(fonte); texte3.setFont(fonte); // Colorer le texte texte1.setColor(couleur); texte2.setColor(couleur); texte3.setColor(couleur); // Style du texte (normal) texte1.setStyle(0); texte2.setStyle(0); texte3.setStyle(0); // Coordonnée X des lignes principales x1=width/2; x2=width/4; x3=3*width/4; // Coordonnée Y des lignes principales et secondaires y1=5.5*heightText; y2=7.5*heightText; } Set(string t1, string t2, string t3) // Constructeur { options=2; // Récupération du nombre d'options selection=1; // Présélection de la première ligne // Contenu du texte texte1.setString(t1); texte2.setString(t2); texte3.setString(t3); } void Cine(int a) // Choisit l'animation à réaliser { /* a : animation à réaliser 0 : standard 1 : entrée en glissant par la droite 2 : sortie en glissant par la gauche 3 : sortie en glissant par la droite */ zTexte=a; // Effectuer l'animation demandée iTexte=0; // Réinitialiser iTexte X=0; // Parce qu'on utilise X dans l'animation // Taille des lignes principales et secondaires calc1=2*heightText/3; texte1.setCharacterSize(heightText); texte2.setCharacterSize(heightText); texte3.setCharacterSize(heightText); // Origine au centre calc1=heightText/2; texte1.setOrigin(texte1.getGlobalBounds().width/2, calc1); texte2.setOrigin(texte2.getGlobalBounds().width/2, calc1); texte3.setOrigin(texte3.getGlobalBounds().width/2, calc1); retourTexte=false; // Témoin en-cours/terminé } int Move(int a) // Change la ligne sélectionnée { // a : mouvement à effectuer (+ = aller à-droite ; 0 = rester ; - = aller à-gauche) // renvoie le numéro de la ligne sélectionnée if (a > 0) // Si l'utilisateur veut descendre dans le menu { selection+=a; // Descendre if (selection > options) {selection=1;} } else if (a < 0) // Si l'utilisateur veut monter dans le menu { selection+=a; // Monter if (selection < 1) {selection=options;} } return(selection); // Renvoyer le numéro de la ligne sélectionnée } bool Texte4Anim(int a) // Animer Texte1 { // a : nommbre d'étapes à passer dans l'animation // renvoie l'état de l'animation (en cours / terminée) iTexte+=a; // Incrémenter l'index d'animation switch (zTexte) { case 0: // Animation standard de la ligne sélectionnée if (iTexte >= 20) {iTexte-=20;} // Ne pas dépasser 20 j=facteurDilatation[iTexte]; // Prendre le facteur switch (selection) // Appliquer l'animation à la ligne de texte sélectionnée { case 1: // Animation de la ligne 2 { texte2.setCharacterSize(j*heightText); texte2.setOrigin(texte2.getGlobalBounds().width/2, texte2.getGlobalBounds().height/2); } break; case 2: // Animation de la ligne 3 { texte3.setCharacterSize(j*heightText); texte3.setOrigin(texte3.getGlobalBounds().width/2, texte3.getGlobalBounds().height/2); // Origine au centre } break; } break; case 1: // Entrée par la droite en glissant if (iTexte >= 20) // Ne pas dépasser 20 { iTexte=19; retourTexte=true; } X=facteurEntree[iTexte]; // Prendre le facteur break; case 2: // Sortie par la gauche en glissant if (iTexte >= 20) // Ne pas dépasser 20 { iTexte=19; retourTexte=true; } X=facteurSortie[iTexte]; // Prendre le facteur break; case 3: // Sortie par la droite en glissant if (iTexte >= 20) // Ne pas dépasser 20 { iTexte=19; retourTexte=true; } X=-facteurSortie[iTexte]; // Prendre le facteur break; } // Positionner texte1.setPosition(x1+X, y1); texte2.setPosition(x2+X, y2); texte3.setPosition(x3+X, y2); // Préparation à l'affichage window.draw(texte1); window.draw(texte2); window.draw(texte3); return(retourTexte); // Animation en cours } }; // Déclarer la fenêtre RenderWindow window; // ET CECI N'EST PAS ACCESSIBLE PAR LES CLASSES // Afficher la fenêtre window.create(VideoMode(width, height), "Jeu", Style::Fullscreen); while (z != 1000) // Le programme se termine lorsque z vaut 1000 { switch (z) // Boucle Z { // Ici le programme principal, mais il est sans importance pour nous } if (load == true) // Chargement pas en cours { window.display(); // Afficher tout le contenu de l'écran window.clear(); // Effacer tout le contenu de la fenêtre time2=clock(); // Réinitialiser le compteur de temps calc2=1000*(time2-time1)/CLOCKS_PER_SEC; // Calcul du temps écoulé pendant le dernier cycle step=1+(calc2-(calc2%33))/33; // Calculer combien de cycles de 0.033 seconde se sont écoulés Sleep(33-(calc2%33)); // Attendre jusqu'à ce que 0.033 seconde (respectivement un multiple) se soit écoulé time1=clock(); // Réinitialiser le compteur de temps } else // Chargement en cours { window.display(); // Afficher tout le contenu de l'écran window.clear(); // Effacer tout le contenu de la fenêtre time2=clock(); // Réinitialiser le compteur de temps calc2=1000*(time2-time1)/CLOCKS_PER_SEC; // Calcul du temps écoulé pendant le dernier cycle if (calc2 < 0.033) // Temps de cycle maximum pas atteint { step=0; time1=clock(); // Réinitialiser le compteur de temps } else // Temps de cycle maximum atteint { step=1+(calc2-(calc2%33))/33; // Calculer combien de cycles de 0.033 seconde se sont écoulés time1=clock(); // Réinitialiser le compteur de temps } } } }
Mon dieu mais c'est quoi ce code
Tu dis que tu ne peux pas passer une RenderWindow en paramètre à un constructeur, est-ce une limitation technique ou autre ? As-tu pensé à la passer par référence ? (& ou *)
Je pense que je pourrais passer une renderWindow comme paramètre, mais le problème (que je pressens) c'est que ça va doubler le flux de sortie (et je suis sûr qu'il y aura des problèmes d'affichage après).
La passer par référence ça consiste en quoi? =)
Tu risques pas d'y arriver, RenderWindow n'est pas copiable, ni assignable.
Par contre tu peux passer ta RenderWindow par référence, c'est à dire qu'au lieu de créer une copie de ta RenderWindow, tu vas (grosso modo) envoyer l'objet original en paramètre, et donc quand le constructeur recevra ton objet, ce sera le même que celui déclaré dans ton main().
Dans cet exemple, l'attribut window n'est pas un objet de type RenderWindow, mais une référence vers un objet de type RenderWindow. Si tu passes la variable que tu as déclaré dans ton main, alors dans les fonctions de Texte4, à chaque fois que tu utiliseras window, tu seras en réalité en train d'utiliser la variable déclarée dans ton main.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class Texte4 { // ... RenderWindow& window; public: Texte4(RenderWindow& w, /* ... */):window(w) { /* ... */ } // ... };
Tu peux trouver de nombreux articles traitant des références et des pointeurs sur le net.
une référence au sens général est un truc manipulant une autre chose.
Un pointeur est une variable contenant l'adresse d'une autre, et se manipule comme telle.
Une référence (C++) est une variable contenant l'adresse d'une autre, et qui se manipule directement comme si elle était cette autre variable.
Regarde la faq correspondante
Mes principes de bases du codeur qui veut pouvoir dormir:Pour faire des graphes, essayez yEd.
- Une variable de moins est une source d'erreur en moins.
- Un pointeur de moins est une montagne d'erreurs en moins.
- Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
- jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
- La plus sotte des questions est celle qu'on ne pose pas.
le ter nel est le titre porté par un de mes personnages de jeu de rôle
Merci, the Hound! Là j'ai une base pour effectuer des recherches.
Ca a l'air utile le passage par références. Je peux aussi passer des tableaux avec ça, au lieu de les copier bêtement quand mes classes en ont besoin pour travailler?
Pour les tableaux, c'est un poil plus complexe, puisque ce sont en réalité des pointeurs et les pointeurs sont des types référence.
Mais comme tu dis, les références c'est extrêmement pratique et utilisé, et ça fait partie des bases du langage C++.
Précisément.
En gros, il y a trois passages possibles d'un type T en argument:
- par valeur: int f(T t);
- par référence constante: int f(T const & t);
- par référence modifiante: int f(T & t);
A chaque fois, une variable locale à la fonction est instanciée, et construite avec l'argument transmis.
Par valeur, il y a appel au constructeur de copie.
Par référence, il y a initialisation de la référence. Seule l'adresse est copiée.
La différence entre référence constante et non constante est double:
- Une référence constante peut référer une valeur temporaire (telle que "bidule" ou 3)
- Une référence constante empêchera toute modification du référé (pas d'affectation pour les types de base ou les membres, pas d'appel aux méthodes non const pour les classes)
En résumé, une référence constante provoque la non-copie de l'argument, mais des indirections à l'usage.
Pour les classes, c'est généralement ce qu'on veut.
Pour les types de bases, c'est discutable.
Mais comme les compilateurs ont le droit de changer le code effectivement compilé tant que "tout se passe comme" le code source, la question est tranchée par la lisibilité du source: pas de référence vers des nombres.
Un pointeur est un type de base, et peut-être transmis comme tel (copié), ou par référence constante ou non.
Cela dit, une référence constante sur un pointeur n'a aucun intérêt.
Autre chose très importante sur les références.
La virtualité des fonctions membres (mot clé virtual) n'est utilisable qu'avec les références et pointeurs
edit: attention, j'avais inversé les exemples des références. C'est corrigé. Merci Médinoc
Mes principes de bases du codeur qui veut pouvoir dormir:Pour faire des graphes, essayez yEd.
- Une variable de moins est une source d'erreur en moins.
- Un pointeur de moins est une montagne d'erreurs en moins.
- Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
- jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
- La plus sotte des questions est celle qu'on ne pose pas.
le ter nel est le titre porté par un de mes personnages de jeu de rôle
Cool. Je marque le sujet comme résolu, et je reviendrai après un peu de lecture. Merci beaucoup!
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager