This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in202:seance_1:td_1 [2021/03/21 11:40] bmonsuez [Question 4.2.1] |
in202:seance_1:td_1 [2022/11/18 10:46] (current) |
||
---|---|---|---|
Line 5: | Line 5: | ||
==== Etape 1 : Installation de l'environnement ==== | ==== Etape 1 : Installation de l'environnement ==== | ||
- | En vous référant à la documentation disponible sous ce lien et en n'hésitant pas à demander à l'enseignant de vous accompagner, installer un environnement de développement sur votre ordinateur. | + | En vous référant à la [[..:outils|documentation disponible]] sous ce lien et en n'hésitant pas à demander à l'enseignant de vous accompagner, installer un environnement de développement sur votre ordinateur. |
==== Etape 2 : Compilation d'un exemple simple de classe C++ === | ==== Etape 2 : Compilation d'un exemple simple de classe C++ === | ||
En fonction de l'environnement, essayer de créer un projet "console" et ensuite ajouter le code suivant dans ce projet. | En fonction de l'environnement, essayer de créer un projet "console" et ensuite ajouter le code suivant dans ce projet. | ||
+ | |||
+ | * Fichier ''rational.hpp''\\ <code cpp> | ||
+ | #ifndef rationalHPP | ||
+ | #define rationalHPP | ||
+ | |||
+ | #include<exception> | ||
+ | #include<stdexcept> | ||
+ | #include<iostream> | ||
+ | |||
+ | class rational | ||
+ | { | ||
+ | private: | ||
+ | long m_numerator; | ||
+ | long m_denominator; | ||
+ | |||
+ | template<typename charT, typename charTraits> | ||
+ | friend inline std::basic_ostream<charT, charTraits>& operator << ( | ||
+ | std::basic_ostream<charT, charTraits>& aStream, const rational& aRational); | ||
+ | |||
+ | public: | ||
+ | rational() : m_numerator(0), m_denominator(1) | ||
+ | {} | ||
+ | rational(long theNumerator) : | ||
+ | m_numerator(theNumerator), m_denominator(1) | ||
+ | {} | ||
+ | rational(long theNumerator, long theDenominator) | ||
+ | { | ||
+ | if (theDenominator == 0) | ||
+ | throw std::invalid_argument("denominator is 0"); | ||
+ | if (theDenominator < 0) | ||
+ | { | ||
+ | theNumerator *= -1; | ||
+ | theDenominator *= -1; | ||
+ | } | ||
+ | m_numerator = theNumerator; | ||
+ | m_denominator = theDenominator; | ||
+ | } | ||
+ | rational(const rational& aRational): | ||
+ | m_numerator(aRational.m_numerator), | ||
+ | m_denominator(aRational.m_denominator) | ||
+ | {} | ||
+ | |||
+ | operator double() const | ||
+ | { | ||
+ | return (double)m_numerator / (double)m_denominator; | ||
+ | } | ||
+ | rational operator + (const rational& anotherRational) const | ||
+ | { | ||
+ | return rational(m_numerator * anotherRational.m_denominator + m_denominator * anotherRational.m_numerator, | ||
+ | m_denominator * m_numerator); | ||
+ | } | ||
+ | rational operator - (const rational& anotherRational) const | ||
+ | { | ||
+ | return rational(m_numerator * anotherRational.m_denominator - m_denominator * anotherRational.m_numerator, | ||
+ | m_denominator * m_numerator); | ||
+ | } | ||
+ | rational operator * (const rational& anotherRational) const | ||
+ | { | ||
+ | return rational(m_numerator * anotherRational.m_numerator, | ||
+ | m_denominator * m_numerator); | ||
+ | } | ||
+ | rational operator / (const rational& anotherRational) const | ||
+ | { | ||
+ | return rational(m_numerator * anotherRational.m_denominator - m_denominator * anotherRational.m_numerator, | ||
+ | m_denominator * m_numerator); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | template<typename charT, typename charTraits> | ||
+ | inline std::basic_ostream<charT, charTraits>& operator << ( | ||
+ | std::basic_ostream<charT, charTraits>& aStream, const rational& aRational) | ||
+ | { | ||
+ | aStream << aRational.m_numerator; | ||
+ | if (aRational.m_denominator != 1) | ||
+ | aStream << "/" << aRational.m_denominator; | ||
+ | return aStream; | ||
+ | } | ||
+ | |||
+ | #endif | ||
+ | </code> | ||
+ | * Fichier ''main.cpp''\\ <code cpp> | ||
+ | #include"rational.hpp" | ||
+ | |||
+ | #include<iostream> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | try | ||
+ | { | ||
+ | rational valueA = { 1, 0 }; | ||
+ | } | ||
+ | catch (std::exception& e) | ||
+ | { | ||
+ | std::cout << e.what() << std::endl; | ||
+ | } | ||
+ | rational valueA = { 4, 3 }; | ||
+ | rational valueB = { 3, 4 }; | ||
+ | |||
+ | std::cout << "A + B = " << (valueA + valueB) << std::endl; | ||
+ | std::cout << "A - B = " << (valueA - valueB) << std::endl; | ||
+ | std::cout << "A * B = " << (valueA * valueB) << std::endl; | ||
+ | std::cout << "A / B = " << (valueA / valueB) << std::endl; | ||
+ | |||
+ | std::cout << "4 * B = " << (4 * valueB) << std::endl; | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
Assurez vous que le projet compile et s'exécute correctement. | Assurez vous que le projet compile et s'exécute correctement. | ||
+ | <hidden Correction> | ||
+ | Il suffit de vérifier que le programme compile et s'exécute correctement. | ||
+ | </hidden> | ||
===== Seconde partie : Création d'une classe "Compteur" en C++ ===== | ===== Seconde partie : Création d'une classe "Compteur" en C++ ===== | ||
- | ==== Question 2.1 : ==== | + | ==== Question 2.1 ==== |
Créer un projet "console" vide dans votre environnement de développement. | Créer un projet "console" vide dans votre environnement de développement. | ||
- | ==== Question 2.2 : ==== | + | <hidden Correction> |
+ | Cela dépend de votre environnement. | ||
+ | </hidden> | ||
+ | |||
+ | ==== Question 2.2 ==== | ||
Ajoutez un fichier ''Counter.h'' ou ''Counter.hpp'' ou ''Counter.hh'' dans votre fichier et introduisez la structure de la classe ''Counter'' dans ce fichier. | Ajoutez un fichier ''Counter.h'' ou ''Counter.hpp'' ou ''Counter.hh'' dans votre fichier et introduisez la structure de la classe ''Counter'' dans ce fichier. | ||
Line 33: | Line 147: | ||
</code> | </code> | ||
- | === Question 2.3 : === | + | <hidden Correction> |
+ | A nouveau, il suffit de créer les deux fichiers. Par contre, il est important de s'assure que le contenu d'un fichier entête (ie. .h, .hh ou .hpp) ne puissent pas être compilé deux fois même s'il est chargé plus d'une fois. Pour ce faire, il est de bonne pratique d'utiliser la structure suivante : | ||
+ | <code cpp> | ||
+ | #ifndef nameHPP | ||
+ | #define nameHPP | ||
+ | |||
+ | // contenu de votre fichier. | ||
+ | #endif | ||
+ | </code> | ||
+ | |||
+ | où ''name'' correspond au nom de votre fichier, "HPP" pour l'extension '.hpp', "H" pour l'extension '.h', ainsi de suite. | ||
+ | |||
+ | La première fois que le fichier est chargé par le compilateur, l'identifiant ''nameHPP'' n'est pas défini, on analyse le code situé après la directive ''#ifndef''. La première ligne suivante : ''#define nameHPP'' définit l'identifiant ''nameHPP'' et puis ensuite le contenu du fichier est analysé. | ||
+ | |||
+ | Si on charge une seconde fois le fichier, l'identifiant ''nameHPP'' est défini, donc le contenu situé entre la directive ''#ifndef nameHPP'' et la directive ''#endif'' est ignoré. Cela empêche d'avoir des erreurs liées au fait que l'on charge le fichier d'entête plus d'une fois. | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | === Question 2.3 === | ||
Ajoutez les fonctions membres à votre compteur : | Ajoutez les fonctions membres à votre compteur : | ||
Line 58: | Line 190: | ||
Expliquer ce que font ces fonctions membres. | Expliquer ce que font ces fonctions membres. | ||
- | === Question 2.4 : === | + | <hidden Correction> |
+ | La première fonction augmente le compteur de un en un tant que la valeur maximale n'est pas atteinte. Si la valeur maximale est atteinte, il recommance à compter à partir de la valeur 0. | ||
+ | |||
+ | La seconde fonction remet le compteur à 0. | ||
+ | </hidden> | ||
+ | |||
+ | === Question 2.4 === | ||
Testez le code avec le programme ''main.cpp'' suivant : | Testez le code avec le programme ''main.cpp'' suivant : | ||
Line 93: | Line 231: | ||
Expliquer le comportement des compteurs. | Expliquer le comportement des compteurs. | ||
+ | <hidden Correction> | ||
+ | Le ''counterA'' a été créé avec ''count'' à 0 et ''max'' à 2. | ||
+ | |||
+ | Le ''counterB'' a été créé avec ''count'' à 0 et ''max'' à 4. | ||
+ | |||
+ | Incrémenter le compteur ''counterA'' va générer la séquence : 0, 1, 2, 0, ... | ||
+ | |||
+ | Incrémenter le compteur ''counterB'' va générer la séquence : 0, 1, 2, 3, ... | ||
+ | |||
+ | </hidden> | ||
===== Troisième partie : Les constructeurs ===== | ===== Troisième partie : Les constructeurs ===== | ||
Line 121: | Line 269: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | <hidden Correction> | ||
+ | Vous constatez que les valeurs des champs ''count'' et ''max'' ont des valeurs qui sortent de nul part. | ||
+ | </hidden> | ||
==== Question 3.2 ==== | ==== Question 3.2 ==== | ||
Line 133: | Line 285: | ||
Ajouter ces constructeurs à votre classe. | Ajouter ces constructeurs à votre classe. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Nous ajoutons les deux constructeurs comme suit: | ||
+ | <code cpp> | ||
+ | class MyCounter | ||
+ | { | ||
+ | public: | ||
+ | int counter; | ||
+ | int max; | ||
+ | | ||
+ | explicit MyCounter(int theMaxValue): count(0), max(theMaxValue) {} | ||
+ | MyCounter(int theCounter, int theMaxValue): count(theCounter), max(theMaxValue) {} | ||
+ | | ||
+ | void increment() { | ||
+ | counter ++; | ||
+ | if(counter > max) | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | void reset() { | ||
+ | counter = 0; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Comme nous ne souhaitons pas une conversion automatique d'un entier en compteur, nous devons mettre ''explicit'' pour empêcher que le compilateur utiliser le constructeur ''MyCounter(int)'' comme constructeur de conversion lui permettant de convertir un entier en un objet de type ''MyCounter''. | ||
+ | |||
+ | </hidden> | ||
=== Question 3.2.2 === | === Question 3.2.2 === | ||
Modifier la fonction ''testCounter'' pour utiliser ces constructeurs afin de créer et d'initialiser les objets ''counterA'' et ''counterB''. | Modifier la fonction ''testCounter'' pour utiliser ces constructeurs afin de créer et d'initialiser les objets ''counterA'' et ''counterB''. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Le code est simplifier, puisqu'il suffit de passer au constructeur la valeur maximale du compteur, supprimant les initialisations des champs ''counter'' et ''max'' qui ne sont désormais plus nécessaires et souhaitables. | ||
+ | |||
+ | <code cpp> | ||
+ | void testCounter() | ||
+ | { | ||
+ | MyCounter counterA(2); | ||
+ | MyCounter counterB(4); | ||
+ | | ||
+ | for(int i = 0; i < 4; i++) | ||
+ | { | ||
+ | std::cout << "compteur A: (" << counterA.counter << ", " << counterA.max << ")" << std::endl; | ||
+ | std::cout << "compteur B: (" << counterB.counter << ", " << counterB.max << ")" << std::endl; | ||
+ | counterA.increment(); | ||
+ | counterB.increment(); | ||
+ | } | ||
+ | } | ||
+ | </hidden> | ||
===== Quatrième partie : Rendre publique/privée des champs et fonctions ===== | ===== Quatrième partie : Rendre publique/privée des champs et fonctions ===== | ||
Line 176: | Line 377: | ||
Modifier votre classe en rendant les champs ''max'' et ''counter'' privés et en déclarant des méthodes d'accès ''getCounter'' et ''getMax''. | Modifier votre classe en rendant les champs ''max'' et ''counter'' privés et en déclarant des méthodes d'accès ''getCounter'' et ''getMax''. | ||
+ | <hidden Correction> | ||
+ | Il suffit de déplacer les deux champs ''counter'' et ''max'' dans la section privée ''private'' de la classe et d'ajouter les méthodes ''int getCounter() const'' et ''int getMax() const'' qui retourne une copie du champ ''counter'' et du champ ''max''. On ajoute le mot clé ''const'' pour indiquer que la méthode ne modifie aucun champ de l'objet et donc ne modifie pas l'objet. | ||
+ | <code cpp> | ||
+ | class MyCounter | ||
+ | { | ||
+ | private: | ||
+ | int counter; | ||
+ | int max; | ||
+ | public: | ||
+ | explicit MyCounter(int theMaxValue): count(0), max(theMaxValue) {} | ||
+ | MyCounter(int theCounter, int theMaxValue): count(theCounter), max(theMaxValue) {} | ||
+ | | ||
+ | int getCounter() const { return counter; } | ||
+ | int getMax() const { return max; } | ||
+ | |||
+ | void increment() { | ||
+ | counter ++; | ||
+ | if(counter > max) | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | void reset() { | ||
+ | counter = 0; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | </hidden> | ||
==== Question 4.2 ==== | ==== Question 4.2 ==== | ||
Vérifier que la compilation de la fonction ''testCounter'' génère des erreurs. | Vérifier que la compilation de la fonction ''testCounter'' génère des erreurs. | ||
+ | |||
+ | <hidden Correction> | ||
+ | Le compilateur indique qu'il n'est pas possible d'accéder aux champs ''counter'' et ''max''. | ||
+ | </hidden> | ||
==== Question 4.3 ==== | ==== Question 4.3 ==== | ||
Line 186: | Line 418: | ||
Modifier la fonction ''testCounter'' pour désormais appeller les méthodes d'accès. Vérifier le bon fonctionnement de la fonction ''testCounter''. | Modifier la fonction ''testCounter'' pour désormais appeller les méthodes d'accès. Vérifier le bon fonctionnement de la fonction ''testCounter''. | ||
+ | <hidden Correction> | ||
+ | |||
+ | Il suffit de remplacer les accès aux champs ''max'' et ''counter'' par des appels aux méthodes ''getMax()'' et ''getCounter()''. | ||
+ | |||
+ | <code cpp> | ||
+ | void testCounter() | ||
+ | { | ||
+ | MyCounter counterA(2); | ||
+ | MyCounter counterB(4); | ||
+ | | ||
+ | for(int i = 0; i < 4; i++) | ||
+ | { | ||
+ | std::cout << "compteur A: (" << counterA.getCounter() << ", " << counterA.getMax() << ")" << std::endl; | ||
+ | std::cout << "compteur B: (" << counterB.getCounter() << ", " << counterB.getMax() << ")" << std::endl; | ||
+ | counterA.increment(); | ||
+ | counterB.increment(); | ||
+ | } | ||
+ | } | ||
+ | </hidden> | ||