Nous allons cette semaine continuer la mise en place des premiers éléments de base de notre projet (qui pour le moment est encore assez général) et réviser (déjà !) la classe des vecteurs à n dimensions à la lumière de nos nouvelles connaissances (constructeurs et surcharge d'opérateurs).
Une fois ces vecteurs révisés, vous allez coder les premières bases pour les objets à simuler: les points matériels et la gravité.
N'oubliez pas tous les conseils donnés la semaine passée :
Il est prévu une répartition des tâches du projet au sein du binôme : chacun doit faire environ la moitié du travail lié au projet ; essayez donc de rapidement vous organiser et concevoir correctement la répartition entre vous ;
par ailleurs, avant de vous lancer directement dans la réalisation de la nouvelle partie du projet, assurez-vous de bien avoir compris les concepts présentés en cours, par exemple en faisant au préalable quelques exercices si nécessaire ;
cette première partie du projet (de la semaine 1 à cette semaine) est assez progressive de sorte à vous permettre de vous
organiser et prendre vos marques dans ce projet ; profitez-en donc pour
faire les choses correctement (ce qui permet de gagner du
temps par la suite) et évitez absolument de prendre du retard...
Je
vous conseille d'avancer le plus possible en synchronisation avec le
planning prévu, ou alors avec au maximum une semaine de décalage pour bien
assimiler le cours.
Pour terminer avec les conseils et remarques préliminaires, n'oubliez pas de revenir de temps en temps consulter la page de description générale du projet et la page d'administration afin de situer votre travail courant par rapport au projet dans son ensemble.
Vecteur
revisitée
Le but de cet exercice est de modifier la classe
Vecteur
commencée il y a deux semaines en ajoutant les
constructeurs et les principaux opérateurs.
Reprenez donc vos fichiers Vecteur.cc
,
Vecteur.h
et testVecteur.cc
.
[ Remarque : les modifications apportées
à un code existant sont typiques de l'évolution d'un logiciel. On
appelle cela le « refactoring » (et il y a des livres entiers dédiés à
ce sujet).
Ce n'est donc pas par erreur de conception du projet
que je vous fait modifier votre code, mais bien pour
(1) voir
l'introduction des nouveaux concepts vus en cours et leur importance,
leurs implications ; et
(2) pratiquer ce type de révisions/modifications
de code (refactoring).
Nous le ferons encore quelques fois au fur et à mesure
des nouveaux concepts. Si votre conception du projet est claire et
cohérente, cela ne devrait pas poser plus de problèmes que de
déplacer/remplacer quelques lignes de code.
]
Définissez (au moins) les constructeurs suivants :
double
comme arguments et construisant
un vecteur 3D à partir de ces coordonnées (utile pour la géométrie) ;
À noter qu'il n'y a donc pas de constructeur par défaut pour cette classe : il faut au moins donner la dimension voulue pour les vecteurs.
[Question P4.1] Avez-vous ajouté un constructeur de copie ? Pourquoi (justifiez votre choix) ?
[Question P4.2] Si l'on souhaitait ajouter un constructeur par coordonnées sphériques (deux angles et une longueur) pour les vecteurs de dimension 3,
Réfléchissez à ces questions et répondez-y dans votre
fichier REPONSES
.
Argumentez vos réponses.
Transformez les méthodes affiche()
et compare()
en des opérateurs.
[Question P4.3] Quels opérateurs avez vous introduits ?
Répondez à cette question dans votre
fichier REPONSES
.
Modifiez votre programme de test dans le même esprit que ci-dessous (cela peut signifier que le code ci-dessous n'est pas forcément 100% juste) :
// un vecteur en 3D : Vecteur vect1(1.0, 2.0, -0.1); // un autre vecteur en 3D Vecteur vect2(2.6, 3.5, 4.1); Vecteur vect3(vect1); // copie de V1 Vecteur vect4(4); // le vecteur nul en 4D cout << "Vecteur 1 : " << vect1 << endl; cout << "Vecteur 2 : " << vect2 << endl; cout << "Vecteur 3 : " << vect3 << endl; cout << "Vecteur 4 : " << vect4 << endl; cout << "Le vecteur 1 est "; if (vect1 == vect2) { cout << "égal au"; } else { cout << "différent du"; } cout << " vecteur 2," << endl << "et est "; if (vect1 != vect3) { cout << "différent du"; } else { cout << "égal au"; } cout << " vecteur 3." << endl;
Continuez votre amélioration du code en utilisant la surcharge
d'opérateurs pour les opérations algébriques de base définies il y a
deux semaines :
transformez en opérateurs toutes les méthodes de la classe Vecteur
qui s'y prettent (p.ex. exceptées la norme euclidienne et son carré).
Pensez aux deux formes des opérateurs : la forme
Op
et la forme Op=
; par exemple, +
et
+=
.
Note : pour le produit vectoriel, utiliser
l'opérateur operator^
.
Notez cependant que cet opérateur a une priorité faible, notamment inférieure à celle de l'opérateur d'affichage <<
. Il faut donc écrire :
cout << (a ^ b);
et non pas
cout << a ^ b;
qui serait compris comme
(cout << a) ^ b;
ce qui n'a aucun sens.
Pour le vecteur unitaire, vous pouvez utiliser l'opérateur (unaire)
operator~
.
Exemple :
Vecteur a({1., 2.5, -3.4}); Vecteur direction( ~a ); // utilisation de l'opérateur unaire ~
Modifiez votre programme de test de sorte à prendre en compte vos modifications (et testez votre programme). Effectuez les mêmes tests que la semaine passée, plus leur version symétrique pour tous les opérateurs commutatifs (c.-à-d. si vous aviez par exemple testé
a.addition(b)
la semaine passée, cela devient cette semaine a + b
; mais il faut aussi tester b + a
).
Le but de cet exercice est de fournir les deux premiers «composants» de notre futur système : les notions de point matériel et de champ gravitationnel terrestre. Nous vous proposons ici de modéliser le champ gravitationnel terrestre comme une classe en tant que telle (plutôt qu'une constante dans un coin) afin de pouvoir, plus tard, généraliser à d'autres champs de forces (comme un champ gravitaionnel quelconque, un champ élétromagnétique, etc.).
On vous demande donc ici de coder deux classes : GravitationConstante
et PointMateriel
.
(Vous pouvez, bien sûr, choisir d'autres noms/d'autres conventions de codage si vous préférez. En termes de langues cependant, merci de coder soit en français, soit en anglais.)
La classe PointMateriel
servira plus tard d'exemple de base (à généraliser) pour d'autres objets mobiles du système.
Elle devra pour le moment posséder au moins les membres suivants :
GravitationConstante
(plus tard cela deviendra un ChampForces
) [ Vous pouvez bien sûr changer les noms proposés
comme bon vous semble et les adapter à votre guise. Si vous le faites,
merci de l'indiquer dans le fichier README
(par exemple
sous forme d'une liste de correspondance entre nom indiqué et nom
choisi). ]
Vous doterez votre classe des constructeurs et des méthodes qui vous semblent adéquat (et du destructeur si nécessaire).
Vous associerez de plus à votre classe une surcharge de l'opérateur
externe <<
permettant d'afficher des
informations de base sur le point matériel (utiles pour «déboguer» le
programme). Vous êtes libres de faire cet affichage selon le
format que vous souhaitez (voir un exemple plus
bas).
Pour le champ de gravité terrestre, créez une classe GravitationConstante
comprenant au minimum :
force()
qui prend un PointMateriel
et un temps comme arguments ;Produisez enfin un programme de test testPointMateriel.cc
dans lequel sont créés un champ de gravité (vertical, d'intensité 9.81 m/s²; voir les constantes dans l'annexe ci-dessous) et
différents points matériels. Afficher ensuite les caractéristiques de tous les objets.
Note : pour l'affichage des PointMateriel
,
nous vous conseillons d'avoir deux méthodes affiche(), une qui ne prend pas d'argument et affiche divers paramètres du PointMateriel
, sauf sa force subie, et une seconde qui prend un temps en paramètre, appelle la première méthode, puis affiche la force subie au temps correspondant.
La surcharge de l'opérateur << n'appellera que la première de ces deux méthodes.
Modifiez le Makefile
pour établir le lien
entre les différents fichiers de votre projet.
NOTE IMPORTANTE : Je rappelle que les programmes de test font pleinement partie du projet et devront être rendus avec le projet.
Voici par exemple ce que pourrait produire votre programme de test:
(le format d'affichage ci-dessous facilitera plus tard la comparaison des sorties de vos programmes aux résultats attendus.)
Nous avons : un champ de force : 0 0 -9.81 # intensite un point matériel : 0.1 # masse 1 2 3 # position 0 0.1 0.2 # vitesse 0 0 -0.981 # force et un autre point matériel : 2 # masse -1.1 1.2 1.3 # position 0.2 0.1 0 # vitesse 0 0 -19.62 # force
Comment correctement faire, sans duplication de code (c.-à-d. sans copie), le partage de constantes, comme par exemple la valeur de g, entre plusieurs fichiers C++ ? En clair : comment partager des variables globales (ouille !!) constantes ?
Tout d'abord un rappel : JAMAIS de variables globales ! C'est vraiment la règle...
...mais comme toute bonne règle, il y a des exceptions (qu'il faut bien comprendre).
Partager la constante g dans un programme de simulation physique est une telle exception car g est à la fois universelle (bien connue bien avant de faire ce programme) et constante.
La meilleure et plus simple façons de faire, mais qui nécessite de compiler au moins avec la norme C++17 (ou au dessus, -std=c++17), c'est simplement de faire un fichier constantes.h
, qui contiendra cette variable et sa valeur :
#pragma once #include "Vecteur.h" namespace votre_choix_de_nom { inline constexpr double cte(2.1967); inline const Vecteur g(0.0, 0.0, -9.81); // ou autre initialisation similaire avec vos constructeurs }
Note : si vous voulez faire des constantes constexpr de vos propres classes (comme p.e.x Vecteur ci-dessus), alors cela supposera d'avoir au moins un constructeur constexpr (mettre constexpr devant le nom du contructeur). Il faut pour cela que ce soit possible (p.ex. avec des vecteurs de taille fixe connue au départ implémentés p.ex. à base de array c'est possible; avec des vecteurs de taille variable implémentés p.ex. à base de vector cela est beaucoup plus compliqué...).
À noter, qu'en effet, il n'y a pas de fichier constantes.cc ici ; le seul .h suffit.
Une autre façon de faire, est de définir un fichier, p.ex. constantes.cc
, qui contiendra effectivement cette variable et sa valeur :
#include "Vecteur.h" extern const Vecteur g(0.0, 0.0, -9.81); // ou autre initialisation similaire avec vos constructeurs
(et c'est tout, rien de plus ici ; si, par contre, vous souhaitez définir d'autres constantes, c'est bien sûr ici qu'il faudrait les mettre, on ne définirait pas un fichier différent par constante !).
Notez bien, cependant, la présence du mot clé extern
, nécessaire en C++ (mais pas en C, pour celles ou ceux qui en auraient fait avant).
Ensuite, bien sûr, il faut le fichier « .h
» correspondant :
#pragma once #include "Vecteur.h" extern const Vecteur g;
Attention ! SANS initialisation ici !!
Et voilà !
Pour ceux qui savent ce que c'est et veulent améliorer leur code : il serait bien sûr préférable de mettre cela dans un namespace
.