Ok! Je vais chercher de ce coté là alors.
Encore une fois, merci![]()
Ok! Je vais chercher de ce coté là alors.
Encore une fois, merci![]()
Les QTimer fonctionnent bien, a priori exactement ce que je recherchais.
Après différents tests annexes, je pense pouvoir me ré-atteler au projet!
Il y a quelques temps (voir page 2):
J'ai pour le moment une fenêtre toute simple, avec un bouton qui appelle le module principal qui devra gérer la succession des différents modules.
J'ai testé cela:
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 #include "FenPrincipale.hpp" FenPrincipale::FenPrincipale() { this->resize(200,100); go = new QPushButton("Go", this); layout = new QVBoxLayout; layout->addWidget(go); setLayout(layout); connect(go, SIGNAL(clicked()), this, SLOT(clicGo())); } void FenPrincipale::clicGo() { QMessageBox::information(this, "Info", "Début du Programme Principal"); Programme_Principal prgm; prgm.execute(); QMessageBox::information(this, "Info", "Fin du Programme Principal"); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #ifndef GLOBALES_H #define GLOBALES_H #include <string> namespace Globales { struct Variables { std::string test; std::string essai; Variables() : test("initialisation de test"), essai("initialisation de essai"){} }; } //namespace Globales #endif // GLOBALES_H
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 #ifndef PROGRAMME_PRINCIPAL_H #define PROGRAMME_PRINCIPAL_H #include "modele/Globales.hpp" struct Globales::Variables; class Programme_Principal { public: Programme_Principal(Globales::Variables & Donnees); void execute(); private: Globales::Variables & donnees; }; #endif // PROGRAMME_PRINCIPAL_HEn somme, le programme n'est sensé rien faire de bien "visuel", mais c'était déjà pour démarrer.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 #include "Programme_Principal.hpp" Programme_Principal::Programme_Principal(Globales::Variables & Donnees):donnees(Donnees){} void Programme_Principal::execute() { }
A l'exécution, sous Qt Creator, j'ai 2 problèmes:
1- ...Programme_Principal.hpp:6: avertissement : declaration 'struct Globales::Variables' does not declare anything
2- ...FenPrincipale.cpp:16: erreur : no matching function for call to 'Programme_Principal:: Programme_Principal()'
Le premier n'est pas bloquant a priori, mais m'intrigue tout de même. J'ai bien défini la structure avant la classe et suivi les conseils de koala01, mais il doit y avoir quelque chose qui m'échappe.
La seconde est logique, puisque je ne suis pas la définition du constructeur, mais... dans FenPrincipale, pas de structure créée ni initialisée, cela se fait dans Programme_Principal, donc comment passer une référence?...
Faut-il créer la structure encore avant? Pour la passer dans le constructeur de Programme_Principal?
Je trouvais intéressant de ne créer la structure qu'une fois dans Programme_Principal, car c'est de lui que tout partira. Donc, en ayant en membre de la classe la structure, il serait facile de la passer aux modules suivants.
Dois-je alors ne pas déclarer la structure avant, ne rien mettre en argument dans le constructeur et créer la structure à l'intérieur de ce dernier pour initialiser le membre?
C'est parce que la ligne
tente de faire une déclation anticipée de la structure Variables se trouvant dans l'espace de noms Globales, alors que... la dite structure est déjà définie dans le fichier globales.h, qui est inclu juste avant la déclaration anticipée
Code : Sélectionner tout - Visualiser dans une fenêtre à part struct Globales::Variables;
[quote]
Les deux possibilités sont valables:La seconde est logique, puisque je ne suis pas la définition du constructeur, mais... dans FenPrincipale, pas de structure créée ni initialisée, cela se fait dans Programme_Principal, donc comment passer une référence?...
Faut-il créer la structure encore avant? Pour la passer dans le constructeur de Programme_Principal?
Je trouvais intéressant de ne créer la structure qu'une fois dans Programme_Principal, car c'est de lui que tout partira. Donc, en ayant en membre de la classe la structure, il serait facile de la passer aux modules suivants.
Dois-je alors ne pas déclarer la structure avant, ne rien mettre en argument dans le constructeur et créer la structure à l'intérieur de ce dernier pour initialiser le membre?
Soit tu crées une variable de type Globales::Variables directement dans main, avant de créer ta variable Program_Principal, et tu transmet la variable au constructeur tel qu'il est (après avoir initialisé la dite variable)
Soit tu supprimes la référence du constructeur, et tu transformes ta référence membre Globales::Variables & donnees; en un membre "simple ( Globales::Variables donnees;), et, bien sur, il faudra penser à faire en sorte que la variable soit correctement initiailsée avant tout usage![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
D'accord, donc c'est soit l'une, soit l'autre, mais pas les deux.
Laquelle privilégier alors?
L'inclusion du fichier ou la déclaration struct?
Par contre dans le .cpp il faut bien remettre l'inclusion?
Je vais prendre la seconde pour ce tout premier module je pense. Comme ca "j'initialise" le tout au début.
Par contre, pour la suite, dois-je faire comme tu m'as conseillé, à savoir passer cette structure en référence à chaque constructeur des classes qui en ont besoin? Avec cela pourrai-je modifier la structure depuis l'extérieur? (permis par la référence?) Dois-je placer la "structure membre" dans la partie publique? Ou dois-je écrire des accesseurs et mutateurs?
Je n'ai pas oublié ce que tu avais dit par rapport aux portées des structures et des classes ainsi que sur les références ^^, mais comme là je mélange le tout... ca fait une mixture pour le moment un peu trouble
Et pour finir:
Comment implémenter le constructeur avec la structure?
J'ai comme unique membre de Programme_Principal "donnees" du type de la structure.
Quand je fais:
Le compilateur ne me renvoie pas d'erreur mais il souligne donnees comme variable inutilisée. Dois-je m'en soucier ou est-ce la bonne méthode?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Programme_Principal::Programme_Principal() { Globales::Variables donnees; }
Utilises la déclaration anticipée chaque fois que tu le peux (c'est à dire : chaque fois qu'il te suffit de connaitre le nom d'une classe ou d'une structure, mais que tu n'essayes pas d'accéder à son conenu dans le fichier dans lelquel se trouve la déclaration anticipée) et l'inclusion du fichier chaque fois que tu le dois (comprends : chaque fois que tu ne déclares pas une référence ou un pointeur et / ou chaque fois que tu vas tenter d'accéder au contenu d'une structure ou d'une classe)
Si le fichier n'est pas déjà inclus (même de manière indirecte), oui, en effetPar contre dans le .cpp il faut bien remettre l'inclusion?
C'est un choixJe vais prendre la seconde pour ce tout premier module je pense. Comme ca "j'initialise" le tout au début.
Normalement, tu ne devrais pas...Par contre, pour la suite, dois-je faire comme tu m'as conseillé, à savoir passer cette structure en référence à chaque constructeur des classes qui en ont besoin? Avec cela pourrai-je modifier la structure depuis l'extérieur?
C'est à l'intérieur de tes modules que les modifications devraient etre apportées, et tu ne devrais donc pas (afin de respecter la loi demeter) essayer d'accéder à ta structure en elle-même
Tu peux le faire, dans le sens où ce n'est pas interdit par le langage...(permis par la référence?)
Par contre, tu ne devrais pas le faire dans le sens où il n'est jamais bon d'exposer plus que nécessaire un "détail d'implémentation.
Normalement, ce devrait etre les fonctions membres de tes différents modules qui s'occupent de manipuler les données comprises dans Globales::VariablesLe mieux encore serait de placer ta structure dans une accessibilité privée et d'éviter le recours aux mutateurs / acesseursDois-je placer la "structure membre" dans la partie publique? Ou dois-je écrire des accesseurs et mutateurs?
Ca se comprendJe n'ai pas oublié ce que tu avais dit par rapport aux portées des structures et des classes ainsi que sur les références ^^, mais comme là je mélange le tout... ca fait une mixture pour le moment un peu trouble![]()
Attention, là, tu déclares une variable de type Globales::Variables qui ne sera utilisable que dans le constructeur, et qui cache le membre de meme nomEt pour finir:
Comment implémenter le constructeur avec la structure?
J'ai comme unique membre de Programme_Principal "donnees" du type de la structure.
Quand je fais:
Le compilateur ne me renvoie pas d'erreur mais il souligne donnees comme variable inutilisée. Dois-je m'en soucier ou est-ce la bonne méthode?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Programme_Principal::Programme_Principal() { Globales::Variables donnees; }
Et, comme tu n'en fais rien, il n'est pas vraiment étonnant que ton EDI t'indique une erreur potentielle (pourquoi créer quelque chose que tu n'utilises plas![]()
)
Ceci dit, le mieux reste toujours d'utilier les liste d'initialisation![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Oui c'est à cela que je pensais. Et donc en passant par référence la structure quand il le faut, ces fonctions membres (ou fonctions de namespaces) pourront modifier le contenu de la structure?
J'ai pensé aux listes d'initialisation, mais tous les exemples que j'ai vu partent de types simples. Puisque ma structure n'est pas créée antérieurement, je ne peux pas la passer dans le constructeur, ainsi, comment faire une liste d'initialisation sans avoir l'objet à mettre dans la variable?
En faisant:
?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Programme_Principal::Programme_Principal():donnees(Globales::Variables()) {}
[quote=Gm7468;6712410]Oui c'est à cela que je pensais. Et donc en passant par référence la structure quand il le faut, ces fonctions membres (ou fonctions de namespaces) pourront modifier le contenu de la structure?
[QUOTE]Oui, tout à fait
Si ta structure dispose d'un constructeur par défaut (ce qui est le cas si tu ne définis pas toi même un constructeur OU si tu définis un constructeur ne prenant aucun argumentJ'ai pensé aux listes d'initialisation, mais tous les exemples que j'ai vu partent de types simples. Puisque ma structure n'est pas créée antérieurement, je ne peux pas la passer dans le constructeur, ainsi, comment faire une liste d'initialisation sans avoir l'objet à mettre dans la variable?
En faisant:
?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Programme_Principal::Programme_Principal():donnees(Globales::Variables()) {}), tu peux éviter le recours à la liste d'initialisation, les membres d'une classe (ou d'une structure) étant automatiquement construits dans l'ordre de leur déclaration (au niveau du constructeur) et automatiquement détruit dans l'ordre inverse de leur déclaration dans le destructeur
![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Ok, je n'avais pas essayé cette possibilité, qui me paraissait "trop simple", et elle fonctionne.
J'ai un constructeur par défaut, qui "initialise" une partie des membres de la structure, et en ne mettant rien pour le constructeur du Programme_Principal (ni liste ni implémentation dans les {}), ca a l'air de fonctionner.
Merci
à bientôt pour de nouvelles questions ^^
Une petite formalité, pour être sur que mon code soit "propre" (quand on en arrive à ces considérations là c'est que ca commence à être assez bien ^^).
Par exemple dans cette fonction toute simple de calcul de la distance d'un point à un plan. Que dois-je déclarer comme constant?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 double const CalculDistancePointPlan(VPoint const & point, double const & a, double const & b, double const & c, double const & d) { double dist; dist = (a*point.getXPoint() + b*point.getYPoint() + c*point.getZPoint() + d)/sqrt(a*a+b*b+c*c); return dist; }
Là j'ai tout mis... mais à mon avis... je dirais qu'une solution suffirait, non?
- Soit déclarer la fonction comme const (double const CalculDistancePointPlan...), et pas les références vers les arguments.
- Soit déclarer les arguments en références constantes ((VPoint const & point, double const & a, double const & b, double const & c, double const & d)) mais pas la fonction.
Par contre, bien entendu, pour une fonction qui modifie l'un de ses arguments, seule la seconde possibilité serait acceptable.
J'en profite pour redemander confirmation, j'ai tout passé par référence, mais seule la référence vers l'objet VPoint serait nécessaire, c'est bien ça? Car pour les types courants, cela n'apporte rien de plus que la copie?
Quelle solution privilégier alors pour ces types? Les références ou vraiment pas?
EDIT:
J'ai un nouveau problème, je pense qu'il est du aux namespaces que j'utilise, mais je n'arrive pas à le résoudre...
Voici une partie des codes:
Fichier Initialisation.hpp
Fichier Initialisation.cpp
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 #ifndef INITIALISATION_H_INCLUDED #define INITIALISATION_H_INCLUDED ... #include "modele/Globales.hpp" ... #include "controleur/Transversaux.hpp" ... namespace Initialisation { void initialisation(Globales::Variables & datas); (...) } //namespace Initialisation #endif // INITIALISATION_H_INCLUDED
Fichier Transversaux.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 #include "Initialisation.hpp" namespace Initialisation { // Module Initialisation void initialisation(Globales::Variables & datas) { (...) General::Transversaux::AlerteMaintenance(datas, "0.4"); (...) } } //namespace Initialisation
Si je commente l'appel à la fonction (General::Transversaux::Alerte...), tout fonctionne. Par contre, si je l'active, lors de la compilation j'ai une erreur:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 (...) namespace General { namespace Transversaux { (...) void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0); (...) } //namespace Transversaux } //namespace General
\Initialisation.cpp:48: erreur : undefined reference to `General::Transversaux::AlerteMaintenance(Globales::Variables&, QString const&, QString const&, int const&)'
D'où cela peut-il venir?
J'ai inclus tous les fichiers nécessaires et je nomme correctement les namespaces...
Pour ce qui est des types primitifs (double, par exemple), si les modifications qu'ils pourraient éventuellement subire dans une fonction ne doivent pas être répercutées dans la fonction appelante, tu peux parfaitement les passer simplement par valeur.
Il n'y a, en effet, aucun intérêt à passer des types primitifs par référence, surtout si les modifications qu'ils subissent dans une fonction ne doivent pas être répercutées dans la fonction appelante
Cela sous entend que, si la fonction ne modifie pas l'objet au départ duquel elle est appelée, elle reste susceptible de modifier les arguments qu'elle reçoit... Est ce ce qui t'intéresse- Soit déclarer la fonction comme const (double const CalculDistancePointPlan...), et pas les références vers les arguments.
Ce qui sous entend que la fonction modifie l'objet au départ duquel elle est appelée (et ne peut donc pas être appelée depuis un objet constant), mais ne modifie pas les arguments qu'elle reçoit... Encore une fois, est-ce ce qui t'intéresse- Soit déclarer les arguments en références constantes ((VPoint const & point, double const & a, double const & b, double const & c, double const & d)) mais pas la fonction.
ou la solution d'une fonction non constante et d'un argument (celui qui doit etre modifiéPar contre, bien entendu, pour une fonction qui modifie l'un de ses arguments, seule la seconde possibilité serait acceptable.) passé par référence non constante, si la fonction modifie l'objet depuis lequel elle est appelée
En effetJ'en profite pour redemander confirmation, j'ai tout passé par référence, mais seule la référence vers l'objet VPoint serait nécessaire, c'est bien ça? Car pour les types courants, cela n'apporte rien de plus que la copie?
généralement le passage par valeur plutot que par référence...Quelle solution privilégier alors pour ces types? Les références ou vraiment pas?
Sauf si la fonction doit modifier la valeur de l'argument et que cette modification doit être répercutée dans la fonction appelante, toujours
As tu bel et bien l'implémentation de AlerteMaintenance dans un fichier *.cpp, est il correctement compilé et fait il partie de ton projetEDIT:
J'ai un nouveau problème, je pense qu'il est du aux namespaces que j'utilise, mais je n'arrive pas à le résoudre...
Voici une partie des codes:
Fichier Initialisation.hpp
Fichier Initialisation.cpp
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 #ifndef INITIALISATION_H_INCLUDED #define INITIALISATION_H_INCLUDED ... #include "modele/Globales.hpp" ... #include "controleur/Transversaux.hpp" ... namespace Initialisation { void initialisation(Globales::Variables & datas); (...) } //namespace Initialisation #endif // INITIALISATION_H_INCLUDED
Fichier Transversaux.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 #include "Initialisation.hpp" namespace Initialisation { // Module Initialisation void initialisation(Globales::Variables & datas) { (...) General::Transversaux::AlerteMaintenance(datas, "0.4"); (...) } } //namespace Initialisation
Si je commente l'appel à la fonction (General::Transversaux::Alerte...), tout fonctionne. Par contre, si je l'active, lors de la compilation j'ai une erreur:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 (...) namespace General { namespace Transversaux { (...) void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0); (...) } //namespace Transversaux } //namespace General
\Initialisation.cpp:48: erreur : undefined reference to `General::Transversaux::AlerteMaintenance(Globales::Variables&, QString const&, QString const&, int const&)'
D'où cela peut-il venir?
J'ai inclus tous les fichiers nécessaires et je nomme correctement les namespaces...![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Je crois que je m'embrouille quand même un peu avec toutes ces notions...
- Une fonction déclarée comme constante ne modifie pas l'objet duquel elle est appelée, mais cela ne suppose rien sur ses arguments?
- Pour les types courants en argument, on utilise donc soit
si l'argument n'a pas à être modifé;
Code : Sélectionner tout - Visualiser dans une fenêtre à part type const argument
soit
s'il doit être modifié.
Code : Sélectionner tout - Visualiser dans une fenêtre à part type & argument
Mais ce que je proposais:
n'a donc aucun sens, puisque je passais en référence (donc pour modification) un objet (de type courant) constant?
Code : Sélectionner tout - Visualiser dans une fenêtre à part type const & argument
oui, dans Transversaux.cpp, qui inclut Transversaux.hpp, fonction dans le namespace General::Transversaux.
Par contre, le déclaration est
et la définition
Code : Sélectionner tout - Visualiser dans une fenêtre à part void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0);
(cf. les arguments par défaut, mais je crois qu'on doit bien faire comme cela)
Code : Sélectionner tout - Visualiser dans une fenêtre à part void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement, int const numPoint){...}
je ne suis pas sur, mais je dirais a priori oui, car j'utilise d'autres fonctions d'un namespace General::XML placé dans Transversaux.hpp et .cpp et celles-ci fonctionnement.
Le pire, c'est que Qt Creator me propose la fonction quand je tape General::Transversaux, donc il devrait la connaitre...
EDIT: à n'y rien comprendre...
J'ai changé la fonction de place, et je l'ai mise temporairement directement dans le namespace Initialisation:
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 #ifndef INITIALISATION_H_INCLUDED #define INITIALISATION_H_INCLUDED #include <QtCore> #include "modele/Globales.hpp" (...) namespace Initialisation { void initialisation(Globales::Variables & datas); void erreurInitiale(Globales::Variables & datas); void chargementXML(QFile & fichierXML, Globales::Variables & datas); void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0); } //namespace Initialisation #endif // INITIALISATION_H_INCLUDEDLes fonctions erreurInitiale() et chargementXML() fonctionnent, mais la fonction AlerteMaintenance() envoie toujours le problème:
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 #include "Initialisation.hpp" namespace Initialisation { // Module Initialisation void initialisation(Globales::Variables & datas) { erreurInitiale(datas); chargementXML(fReturnCodes, datas); AlerteMaintenance(datas, "0.4"); } // Sous-Module Erreur Initiale void erreurInitiale(Globales::Variables & datas) { //actions... } // Sous-Module Chargement XML void chargementXML(QFile & fichierXML, Globales::Variables & datas) { //actions... } void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement, int const numPoint) { //actions... } } //namespace Initialisation
Initialisation.cpp:47: erreur : undefined reference to `Initialisation::AlerteMaintenance(Globales::Variables&, QString const&, QString const&, int const&)'
Alors que cette fois on est dans le même namespace, avec des fonctions similaires qui fonctionnent!
EDIT 2: résolu mais toujours pas compris...
J'ai tout replacé dans Transversaux.hpp et .cpp.
Et j'ai tout ré-écrit. Par contre cette fois, le compilateur m'a proposé quelque chose d'étrange:
Déclaration de la fonction dans .hpp:
Comme je l'avais définie dans le .cpp précédemment: (qui posait problème)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0);
Ce que le compilateur m'a proposé: (qui, à présent, fonctionne)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement, int const numPoint)
-> Remarquer le passage de type const & argument à const type &argument
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables &datas, const QString &code, const QString &supplement, const int &numPoint)
(position du const et espace entre le & et l'argument).
Toutes mes autres fonctions ont été faites comme la première solution, qui là n'a pas fonctionné. Pourquoi?
EDIT 3: Compris!
C'est tout simple: j'avais oublié un "&" pour le dernier argument, cette fois ca marche avec la première solution.
Je suis tout de même curieux (cf. titre du post), de savoir quelle solution est à appliquer:
- type const & argument (comme je le fais)
ou
- const type &argument (comme me le propose le compilateur)
Et à ce moment il faudrait appliquer la même chose pour la déclaration (.hpp) et la définition (.cpp)?
Me revoilà pour une nouvelle question, en plus des précédentes
J'ai une classe d'objet qui fait pas mal d'actions plus ou moins complexes et élémentaires. Pour "simplifier" le fonctionnement, je me suis dit qu'il serait judicieux de séparer ces actions élémentaires de la classe. Je les ai donc placées dans un namespace dans d'autres fichiers.
J'ai donc:
Objet.hpp
Objet.cpp
Namespace.hpp
Namespace.cpp
Certaines fonctions du Namespace nécessitent l'objet, j'ai donc inclus Objet.hpp dans le Namespace.hpp. Jusque là aucun problème.
Par contre, ce que j'aimerais faire, c'est utiliser ces fonctions élémentaires de Namespace dans mon objet.
Et donc je devrais aussi inclure Namespace.hpp dans Objet.hpp.
Cependant, lorsque je fais cela, toutes les fonctions de Namespace qui utilisent objet envoient des problèmes:
- variable or field 'Fonction' declared void
- 'Objet' was not declared in this scope
Ce problème vient lorsque j'inclus Namespace.hpp dans Objet.hpp, alors que Objet.hpp est déjà inclus dans Namespace.hpp.
Est-ce que le C++ interdit l'inter-inclusion comme cela?
A ce moment y a-t-il une solution?
Ou dois-je revenir à mon ancienne architecture sans le namespace?
EDIT:
Début de solution trouvée, mais encore une fois, je ne comprends pas bien, si on peut m'éclairer
Dans le Namespace.hpp:
Comme cela, le compilateur ne m'envoie plus de problème.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 #include Objet.hpp class Objet; namespace Namespace{ ... }
Mais pourquoi?
C'est la même histoire qu'avec la structure, mais je ne comprends pas pourquoi le include ne suffit pas, et pourquoi il faut les deux... et uniquement dans le .hpp.
Exactement : elle s'engage uniquement à ne pas modifier l'objet au départ duquel elle est invoquée, mais ne s'engage à rien par rapport aux objets impactés par les arguments qu'elle reçoit.
Si les objet impactés par les arguments qu'elle reçoit ne doivent pas être modifiés, il s'agira:
de déclarer l'objet en question comme étant constant
de veiller à ce que les éventuels accesseurs utilisés soient constants (c'est de toutes manières une quasi obligation) et renvoient une référence constante (ou une valeur, qui, du coup, évitera les répercussions sur l'objet d'origine)
==> avec comme résultat que la fonction ne pourra pas recevoir autre chose qu'une valeur (donc, sans répercussion sur l'objet d'origine) ou une référence... constante
Je présumes que tu veux dire "type primitif" (AKA char, short, int, long, long long, leur équivalent unsigned, float, double et long double)- Pour les types courants en argument, on utilise donc soit
si l'argument n'a pas à être modifé;
Code : Sélectionner tout - Visualiser dans une fenêtre à part type const argument
soit
s'il doit être modifié.
Code : Sélectionner tout - Visualiser dans une fenêtre à part type & argument
Mais ce que je proposais:
n'a donc aucun sens, puisque je passais en référence (donc pour modification) un objet (de type courant) constant?
Code : Sélectionner tout - Visualiser dans une fenêtre à part type const & argument
Mais oui, en effet, il est purement interdit d'essayer de modifier un objet passé sous forme constante (que ce soit par valeur ou référence constante): le compilateur t'insultera si tu essayes de le faire
Oui, numPoint est déclaré sous la forme d'un int const d'un coté et sous celle d'un int const & de l'autre...oui, dans Transversaux.cpp, qui inclut Transversaux.hpp, fonction dans le namespace General::Transversaux.
Par contre, le déclaration est
et la définition
Code : Sélectionner tout - Visualiser dans une fenêtre à part void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0);
(cf. les arguments par défaut, mais je crois qu'on doit bien faire comme cela)
Code : Sélectionner tout - Visualiser dans une fenêtre à part void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement, int const numPoint){...}
L'éditeur de liens cherche donc la fonction prenant une référence constante sur un entier, qu'il ne trouve pas, vu qu'elle est implémentée comme étant une fonction prenant... un entier constant
EDIT 2: résolu mais toujours pas compris...
J'ai tout replacé dans Transversaux.hpp et .cpp.
Et j'ai tout ré-écrit. Par contre cette fois, le compilateur m'a proposé quelque chose d'étrange:
Déclaration de la fonction dans .hpp:
Comme je l'avais définie dans le .cpp précédemment: (qui posait problème)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement = "", int const & numPoint = 0);
Ce que le compilateur m'a proposé: (qui, à présent, fonctionne)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables & datas, QString const & code, QString const & supplement, int const numPoint)
-> Remarquer le passage de type const & argument à const type &argument
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 void AlerteMaintenance(Globales::Variables &datas, const QString &code, const QString &supplement, const int &numPoint)
(position du const et espace entre le & et l'argument).
Toutes mes autres fonctions ont été faites comme la première solution, qui là n'a pas fonctionné. Pourquoi?
La norme précise que le mot clé const s'applique à ce qui se trouve à sa gauche, sauf s'il n'y a rien à gauche, et dans ce cas, il s'applique à ce qui se trouve à sa droite.EDIT 3: Compris!
C'est tout simple: j'avais oublié un "&" pour le dernier argument, cette fois ca marche avec la première solution.
Je suis tout de même curieux (cf. titre du post), de savoir quelle solution est à appliquer:
- type const & argument (comme je le fais)
ou
- const type &argument (comme me le propose le compilateur)
Les deux versions sont donc totalement valide, à la seule différence que la pemière utilise la "règle générale" et la seconde utilise "l'exception à la règle".
Certains (comme moi, mais je suis peut etre une exception) préféreront utiliser la règle générale, et d'autres l'exception
Du point de vue du langage en lui-même, tu peux parfaitement utiliser l'un dans la déclaration et l'autre dans la définition, voir, meme, décider d'utiliser l'un ou l'autre selon "l'air du temps" ou la direction du ventEt à ce moment il faudrait appliquer la même chose pour la déclaration (.hpp) et la définition (.cpp)?
Cependant, il faut bien etre conscient que l'idéal est toujours de garder une certaine homogénéité, au minimum au niveau d'un projet donné![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Merci
Mais donc, cela veut dire que pour déclarer quelque chose en const, la norme voudrait que le mot clé soit toujours tout à droite?
C'est-à-dire:
pour une fonction:
et pour une variable:
Code : Sélectionner tout - Visualiser dans une fenêtre à part double fonction() const;
Ou bien seul le type doit être déclaré constant?
Code : Sélectionner tout - Visualiser dans une fenêtre à part double variable const;
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const fonction();Dans ce second cas, est-ce la règle de la norme ou l'exception qui est appliquée?
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const variable;
Puisqu'il y a quelque chose à gauche, c'est le type, et quelque chose à droite, le nom, mais comme la gauche n'est pas vide, const ne s'applique pas au nom...
Donc... première ou seconde solution?
Sachant que comme tu l'as dit, l'essentiel est d'être cohérent, et pour le moment j'ai tout fait comme la seconde solution, donc c'est cohérent. Mais l'important pour moi est, d'une part de comprendre, et d'autre part que ce soit juste.
En plus de la question précédente, voici une nouvelle question.
Elle a toujours attrait au mécanisme de signaux et slots. C'est vrai que j'ai à présent intégré le système de déclenchement action/réaction, mais je souhaiterais aller plus loin, si cela est possible.
Suivant les conseils de koala01, j'ai le fonctionnement suivant, par exemple pour le déplacement du bras:
Lorsque le bras a à être déplacé, on appelle la fonction deplaceBras(), celle-ci s'occupe de lancer la requête de placement par le port COM, et de démarrer le timer de contrôle.
2 possibilités sont alors envisagées: soit on arrive à la fin du timer (il y a eu un problème lors du placement), soit le placement s'est bien effectué, on a reçu la réponse du bras disant "je suis placé".
L'une ou l'autre des solutions aboutit donc sur une dernière fonction qui gère le résultat, par exemple 0 si tout a bien été, 1 si le timer est arrivé au bout.
Et voici donc ma question:
N'est-il pas possible de concentrer le tout dans une seule fonction?
A savoir, par exemple:
C'est-à-dire, n'y a-t-il pas moyen d'englober le système de signaux/slots à l'intérieur même d'une fonction, pour que, à partir de l'extérieur, on ait juste à appeler placementDuBras(), sans se soucier des signaux et slots émis/reçus et du fonctionnement interne?
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 int placementDuBras(){ deplaceBras(); //attente du signal?... return gestionResultat(reponse); } void deplaceBras(){ timer.start(); //envoie requête de placement par port COM } int gestionResultat(string reponse){ if (reponse == "fin timer") { return 1;} else{ return 0;} }
J'ai pensé à un timer "interne", qui permet de vérifier à intervalles réguliers l'état d'une variable qui sera modifiée lors de l'émission d'une des solutions (fin timer ou réception réponse). On attendrait donc effectivement tant qu'un des deux évènements n'ait pas eu lieu. Mais je trouve cette solution un peu archaïque et pas très optimisée...
Je m'explique sur cette question:
Toujours dans mon projet conséquent, j'ai à commander des actions par communications sur un port COM.
J'ai donc une classe spéciale héritant de QextSerialPort, une classe spécifique développée à coté de Qt pour gérer les interfaces série. Cette classe contient différentes fonctions séparées en éléments simples:
- Création de la requête (chaine de caractères)
- Envoi de la requête (émission de la chaine sur le port COM)
- Réception de données sur le port COM (chaine de caractères)
- Traitement de la réponse (types divers)
J'ai donc 2 possibilités (en tout cas d'après ce que j'envisage) pour faire les actions que je souhaite:
1* soit je passe par une succession de signaux/slots
2* soit je fais des fonctions spécifiques qui englobent ces signaux/slots
Le problème avec la solution 1*, c'est que ca me fait une quantité gigantesque de signaux et slots à gérer ou alors de paramètres pour les gérer. Cela est du au fait que la communication COM sert à beaucoup de choses, et est utilisée depuis plusieurs endroits (modules). La réception et le traitement d'une même réponse peuvent donc, suivant les cas, appeler la suite d'une fonction ou d'une autre.
J'ai donc imaginé (je l'espère), que la solution 2* serait plus simple à utiliser.
Par exemple pour un simple test de la connexion COM, je devrais:
en solution 1*:
Envoyer la requête, et spécifier quelque part l'endroit d'où elle est envoyée. Lancer le timer. Gérer la réponse, soit fin timer, soit réponse reçue. Puis gérer l'appelant d'après ce qui a été spécifié tout à l'heure, puis émettre un signal, fonction de l'appelant, pour que tel ou tel appelant implique tel ou tel signal connecté à telle ou telle suite.
en solution 2*:
J'aurais une fonction pour chaque action que je souhaite réaliser. Par exemple testConnexion(), qui renverrait un entier d'après la réponse. Cette fonction en elle-même ferait toute les actions décrites en 1*, mais sans se soucier de l'appelant, puisqu'elle s'exécuterait directement dans cet appelant.
Cela me permettrait donc d'une part de simplifier la gestion des signaux/slots en évitant la foultitude de possibilités, et d'autre part, de structurer les actions réalisées de manière propre.
Ainsi, pour des successions d'actions, je pourrais faire:
directement là où j'en ait besoin;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 action1(); action2(); action3();
au lieu de:
et ce pour chaque action envisagée, avec un appelant qui hérite lui aussi de QObject pour pouvoir intégrer les signaux émis par l'objet de port COM.
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 requete1(); //signal de réception connecté à traitement traitement(id){ if(...){emit resultat1;} else if(...){emit resultat2;} else if(...){emit resultat3;} } //signaux résultat 1, 2 et 3 connectés à resultat(id, appelant) resultat(id, appelant){ if(appelant == ...){... emit retourappelant1;} else if(appelant == ...){ ... emit retourappelant2;} }
Pour info, j'ai trouvé une classe extrêmement intéressante, à vrai dire exactement ce que je recherchais: QxtSignalWaiter. Elle permet de faire ce que j'ai matérialisé dans le code par //attente du signal?....
On connecte l'objet à un signal, et un timer est intégré. Dans le code, on lance le timer de l'objet, et 2 possibilités de sortie sont présentes, soit la fin du timer, soit la réception du signal.
Le post précédent est donc résolu, je vais faire mes petites fonctions qui regroupent le tout, avec le fonctionnement signaux/slots mais à l'intérieur même de la fonction, ce qui est rendu possible par cette nouvelle classe.
Une nouvelle question me vient cependant à l'esprit:
Le code source n'est a priori plus maintenu, mais cela pose-t-il un problème?
La classe fonctionne, le code est en "free software", et disponible sur internet. Au-delà de cela je n'ai rien à me soucier?
La question du post d'avant est toujours sans éclaircissement
Et cette fois uniquement une confirmation.
J'ai quelque part dans un namespace Namespace une fonction:
Dans la classe Objet, j'aimerais utiliser cette fonction, j'ai donc une seconde fonction:
Code : Sélectionner tout - Visualiser dans une fenêtre à part void fonctionUne(Objet & obj);
Avec le this tout simple, ca ne fonctionnait pas, normal, puisque la fonction demande une référence vers un objet de type Objet et je lui envoyais un pointeur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 void fonctionDeux(){ ... Namespace::fonctionUne(*this); ... }
Pouvez-vous me confirmer que le *this est bien ce qu'il faut? (déréférencement du pointeur pour avoir un objet, ok, mais je n'envoie quand même pas une référence...)
Pour les variables et les arguments, c'est le type qui doit etre déclaré constant, pour les fonctions memvre, c'est la fonction
Je répète ce que dit la norme
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const fonction();
A ton avis, à quoi s'appliquerait le mot clé const iciEnvoyé par "la norme
C'est bien ca
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const variable;
cf ma question ci-dessusDans ce second cas, est-ce la règle de la norme ou l'exception qui est appliquée?
Pour être clair :Sachant que comme tu l'as dit, l'essentiel est d'être cohérent, et pour le moment j'ai tout fait comme la seconde solution, donc c'est cohérent. Mais l'important pour moi est, d'une part de comprendre, et d'autre part que ce soit juste.
sont acceptés
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 const type /* & / * */ variable const type /* & / * */ argument type const /* & / * */ variable type const /* & / * */ argument Type MaClass::fonction() const
s'applique à la valeur de retour
Code : Sélectionner tout - Visualiser dans une fenêtre à part Type const /* & / * */ fonction()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
D'accord, c'est bien ce qui m'a semblé, donc finalement je fais... n'importe quoi.
Quelle est la différence entre type const fonction();
et type fonction() const; ?
La seconde est lorsque la fonction ne modifie pas l'objet duquel elle est appelée, la première... défini le résultat comme constant?
Mais quel est l'intérêt alors de déclarer une sortie de fonction constante, puisqu'on la met forcément dans une variable qui sera elle modifiée par la suite au besoin?
Donc finalement pour résumer, et je me tiendrai à présent à cela:
Pour les variables et arguments, c'est le type qui doit être déclaré constant (et pas le nom donc), exemple:
Pour les fonctions, c'est toute la fonction, exemple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const variable;
Cette dernière ligne n'a de sens que pour les fonctions membres.
Code : Sélectionner tout - Visualiser dans une fenêtre à part double fonction() const;
Par conséquent, pour des fonctions situées dans un namespace,
est complètement faux;
Code : Sélectionner tout - Visualiser dans une fenêtre à part double fonction() const;
est inutile.
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const fonction();
C'est bien cela?
C'est bien cela
Si tu renvoies le résultat par valeur, presque aucun (hormis celui de "documenter" le fait que la valeur renvoyée n'est pas sensée être modifiée par la suite).Mais quel est l'intérêt alors de déclarer une sortie de fonction constante, puisqu'on la met forcément dans une variable qui sera elle modifiée par la suite au besoin?
Mais, dés que le retour de la fonction est une référence ou un pointeur sur quelque chose, l'intérêt est immense car il implique que tu ne pourras pas modifier le résultat de la fonction, et donc, cela garanti (entre autre) qu'une fonction déclarée constante ne pourra en aucun cas modifier l'objet depuis lequel elle est appelée, même de manière indirecte (par exemple : parce que l'on essayerait de modifier le résultat renvoyé par la fonction)
Cela entre, finalement, dans le cadre du stricte respect de la const correctness
T'as tout comprisDonc finalement pour résumer, et je me tiendrai à présent à cela:
Pour les variables et arguments, c'est le type qui doit être déclaré constant (et pas le nom donc), exemple:
Pour les fonctions, c'est toute la fonction, exemple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const variable;
Cette dernière ligne n'a de sens que pour les fonctions membres.
Code : Sélectionner tout - Visualiser dans une fenêtre à part double fonction() const;
Par conséquent, pour des fonctions situées dans un namespace,
est complètement faux;
Code : Sélectionner tout - Visualiser dans une fenêtre à part double fonction() const;![]()
Meme pas (cf quelques lignes plus haut), et, surement pas si c'estest inutile.
Code : Sélectionner tout - Visualiser dans une fenêtre à part double const fonction();
Code : Sélectionner tout - Visualiser dans une fenêtre à part UnTypeQuelconque const & fonctions();![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Ok d'accord, encore une notion qui s'éclaircit petit à petit
Merci à toi
à bientôt
Partager