Projet : étape 3 (semaines 4 et 5 du semestre)

Buts

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é.

Préliminaires :

N'oubliez pas tous les conseils donnés la semaine passée :


[1*] Exercice P4 : la classe 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.
]

P4.1 Constructeurs

Définissez (au moins) les constructeurs suivants :

  1. le constructeur qui crée un vecteur nul pour la dimension passée en argument ;
  2. un constructeur par coordonnées cartésiennes, prenant trois double comme arguments et construisant un vecteur 3D à partir de ces coordonnées (utile pour la géométrie) ;
  3. [optionnel C++ 2011, mais bien pratique] un constructeur prenant une liste d'initialisation (cf cours) permettant de donner les valeurs voulues (le vecteur aura alors comme dimension la taille de la liste) ;
  4. le constructeur de copie, si vous pensez que cela est nécessaire.

À 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.

P4.2 Opérateurs

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;

P4.3 Opérateurs algébriques

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).


[1*] Exercice P5 : Point matériel et gravité

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 :

[ 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 :

Tests

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.

Exemple de tests

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

Annexe : comment partager des constantes entre plusieurs fichiers ?

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.

Depuis C++ 17

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.

Avant C++ 17

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.


Dernière mise à jour le 29 mars 2025
Last modified: Sat Mar 29, 2025