User Tools

Site Tools


in204:tds:sujets:td4:part2

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
in204:tds:sujets:td4:part2 [2019/10/15 10:38]
bmonsuez [Question 1.0]
in204:tds:sujets:td4:part2 [2022/11/18 10:49] (current)
Line 107: Line 107:
  
 <hidden Correction Complétion de la classe interval>​ <hidden Correction Complétion de la classe interval>​
 +
 Nous débutons par compléter la classe ''​interval''​. La classe ''​interval''​ doit exposer au moins deux méthodes que sont les méthodes :  Nous débutons par compléter la classe ''​interval''​. La classe ''​interval''​ doit exposer au moins deux méthodes que sont les méthodes : 
  
Line 207: Line 208:
  
  \\  \\
- 
  
 </​hidden>​ </​hidden>​
  
 <hidden Correction Complétion de la classe interval_iterator>​ <hidden Correction Complétion de la classe interval_iterator>​
 +<nodisp 2>
  
 Pour la classe ''​interval_iterator''​ nous devons implanter les exigences relatives à un [[https://​en.cppreference.com/​w/​cpp/​named_req/​ForwardIterator|forward iterator]]. ​ Pour la classe ''​interval_iterator''​ nous devons implanter les exigences relatives à un [[https://​en.cppreference.com/​w/​cpp/​named_req/​ForwardIterator|forward iterator]]. ​
Line 217: Line 218:
 \\ \\
  
-  - En ce qui concerne les définitions de type, il est attendu que l'​itérateur expose les types suivants : \\<code cpp>+  - En ce qui concerne les définitions de type, il est attendu que l'​itérateur expose les types suivants : \\ <code cpp>
 difference_type // un type d'​entier signé servant à indiquer la distance entre deux itérateurs ​ difference_type // un type d'​entier signé servant à indiquer la distance entre deux itérateurs ​
 value_type // le type des valeurs qui sont référencés par l'​itérateur value_type // le type des valeurs qui sont référencés par l'​itérateur
Line 226: Line 227:
                 // habituellement ''​T&''​                 // habituellement ''​T&''​
 iterator_category // Le type qui indique la nature de l'​itérateur,​ comme [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​random_access_iterator_tag]],​ [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​bidirectional_iterator_tag]],​ [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​forward_iterator_tag]] ​ ou enfin [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​input_iterator_tag]]. iterator_category // Le type qui indique la nature de l'​itérateur,​ comme [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​random_access_iterator_tag]],​ [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​bidirectional_iterator_tag]],​ [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​forward_iterator_tag]] ​ ou enfin [https://​en.cppreference.com/​w/​cpp/​iterator/​iterator_tags|std::​input_iterator_tag]].
-</​code>​ \\  \\Pour effectuer la définition de ces types, nous pouvons soit les définir directement (attention: les valeurs stockées dans l'​interval n'est pas modifiable, il s'agit donc de ''​const int''​. : \\<code cpp>+</​code>​ \\  \\ Pour effectuer la définition de ces types, nous pouvons soit les définir directement (attention: les valeurs stockées dans l'​interval n'est pas modifiable, il s'agit donc de ''​const int''​. : \\ <code cpp>
 class interval_iterator class interval_iterator
 { {
Line 236: Line 237:
     typedef std::​forward_iterator_tag iterator_category;​     typedef std::​forward_iterator_tag iterator_category;​
 }; };
-</​code>​ \\  \\On peut voir que l'on aurait pu faire références aux définitions faites dans la classe ''​interval'',​ ce qui aurait donné le code suivant :  \\<code cpp>+</​code>​ \\  \\ On peut voir que l'on aurait pu faire références aux définitions faites dans la classe ''​interval'',​ ce qui aurait donné le code suivant :  \\ <code cpp>
 class interval_iterator class interval_iterator
 { {
Line 246: Line 247:
     typedef std::​forward_iterator_tag iterator_category;​     typedef std::​forward_iterator_tag iterator_category;​
 }; };
-</​code> ​ \\ L'​avantage est que dans ce cas là, si nous modifions le type manipulé par la classe ''​interval'',​ automatiquement,​ les types sont bien mis à jour dans la classe ''​interval_iterator'' ​ \\  \\ Pour éviter d'​avoir trop à écrire, nous pouvons au contraire ​ hériter de la classe [[https://​en.cppreference.com/​w/​cpp/​iterator/​iterator|std::​iterator]] qui fournit une implantation par défaut de ces types. ​ ​\\ ​ ​\\<​code cpp>+</​code> ​ \\ L'​avantage est que dans ce cas là, si nous modifions le type manipulé par la classe ''​interval'',​ automatiquement,​ les types sont bien mis à jour dans la classe ''​interval_iterator'' ​ \\  \\ Pour éviter d'​avoir trop à écrire, nous pouvons au contraire ​ hériter de la classe [[https://​en.cppreference.com/​w/​cpp/​iterator/​iterator|std::​iterator]] qui fournit une implantation par défaut de ces types. ​ \\ <code cpp>
 class interval_iterator: ​ class interval_iterator: ​
     std::​iterator<​const int, std::​forward_iterator_tag>​     std::​iterator<​const int, std::​forward_iterator_tag>​
Line 253: Line 254:
    ...    ...
 }; };
-</​code> ​\\ \\Cependant cette méthode est déclarée obsolète depuis l'​introduction de C++17.+</​code>​ \\ Cependant cette méthode est déclarée obsolète depuis l'​introduction de C++17. 
 +  - En ce qui concerne les déclarations des opérateurs et fonctions membres, nous devons définir les opérateurs suivants : \\ <code cpp>
  
-  - En ce qui concerne les déclarations de  +reference ​operator*();​ // retourne la valeur à laquelle l'​itérateur fait référence.
- +
-<code cpp> +
- +
-value_type ​operator*() ​const; // retourne la valeur à laquelle l'​itérateur fait référence.+
 pointer operator->​(); ​          // retourne un pointeur sur la valeur à laquelle l'​itérateur fait référence. pointer operator->​(); ​          // retourne un pointeur sur la valeur à laquelle l'​itérateur fait référence.
 iterator&​ operator++(); ​        // avance à la valeur suivante de la séquence et retourne l'​itérateur courant iterator&​ operator++(); ​        // avance à la valeur suivante de la séquence et retourne l'​itérateur courant
Line 272: Line 270:
                                 // ne font pas référence à la même valeur,                                 // ne font pas référence à la même valeur,
                                 ​                                 ​
 +</​code>​ \\ En ce qui concerne l'​opérateur ''​*'',​ il s'agit de l'​opération de déréférencement qui va retourner une référence sur la valeur à laquelle l'​itérateur fait référence et qui est défini par ''​mCurrent''​. Le code de cette fonction s'​écrit : <code cpp>
 +    reference operator*() const 
 +    {
 +        return mCurrent;
 +    }
 +</​code>​ \\ Par similitude l'​opérateur ''​->''​ qui retourne un pointeur sur la valeur référencée par l'​itérateur va retourner un pointeur sur le champ interne ''​mCurrent''​. \\ <code cpp>
 +    reference operator->​() const 
 +    {
 +        return &​mCurrent;​
 +    }
 +</​code>​ \\ \\ Il faut désormais implanter les opérateurs d'​incrémentation de l'​itérateur (ie. pour passer à la valeur suivante). C'est l'​opérateur ''​++''​ qui a est sélectionné,​ cependant cet opérateur possède deux syntaxes : (1) ''​++it''​ qui signifie applique l'​opération ''​++''​ à l'​objet ''​it''​ et retourne une référence à cet objet après que l'​opération ''​++''​ a été effectuée; (2) ''​it++''​ qui signifie fait une copie de l'​objet //copy//, applique l'​opération à l'​objet ''​it''​ et retourne l'​objet //copy// comme résultat de l'​opération. L'​opérateur ''​operator ++()''​ correspond à la première synataxe, l'​opérateur ''​operator ++(int)''​ correspond à la seconde syntaxe. Ce qui se traduit par : <​code>​
 +    iterator&​ operator++()
 +    {
 +        // Passe à la valeur suivant si nous ne sommes pas
 +        // déjà à la fin de l'​interval.
 +        if(mCurrent <= mInterval->​maxValue)
 +            mCurrent ++;
 +        return *this;
 +    }
 +    iterator operator++(int)
 +    {
 +        interval_iterator it(*this); // Crée une copie de l'​itérateur.
 +        ++(*this); ​         // Incrément l'​itérateur courant en appellant
 +                            // l'​opérateur iterator&​ operator()
 +        return it;          // Retourne la copie de l'​itérateur (avant modification).
 +    }
 +</​code>​ \\ Il ne reste plus qu'à implanter les opérateurs relatifs à la comparaison. Deux itérateurs sont égaux s'ils font références à la même valeur située dans le même containeur. Dans ce cas, nous devons avoir les champs ''​mCurrent''​ et ''​mInterval''​ qui doivent être égaux. <code cpp>
 +    bool operator ==(const interval_iterator&​ anotherIterator) const
 +    {
 +        return mCurrent == anotherIterator.mCurrent && ​
 +          *mInterval == *anotherIterator.mInterval;​
 +    }
 +    bool operator !=(const interval_iterator&​ anotherIterator) const
 +    {
 +        return mCurrent != anotherIterator.mCurrent || 
 +          *mInterval != *anotherIterator.mInterval;​
 +    }
 </​code>​ </​code>​
 +  - Enfin, l'​itérateur doit aussi être [[https://​en.cppreference.com/​w/​cpp/​named_req/​CopyConstructible|constructible par recopie]], ​ [[https://​en.cppreference.com/​w/​cpp/​named_req/​CopyAssignable|supporter l'​affection]] et [[https://​en.cppreference.com/​w/​cpp/​named_req/​Swappable|supporter les permutations]]),​ ce qui impose de définir en complément le constructeur de recopie, les fonctions membres ''​swap''​ et l'​opérateur ''​=''​ comme suit: \\ <code cpp>
 +public:
 +    // Définition du constructeur de recopie.
 +    // Crée un itérateur qui fait référence au même élément que l'​itérateur
 +    // passé en argument.
 +    interval_iterator(const interval_iterator&​ anotherIterator):​
 +        mCurrent(anotherIterator.mCurrent),​ mInterval(anotherIterator.mInterval)
 +    {}
 +    // Définition de l'​opération d'​affectation consistant à ce que 
 +    // l'​itérateur courant fait référence au même élément que l'​itérateur
 +    // passé en argument.
 +    interval_iterator&​ operator=(const interval_iterator&​ anotherIterator)
 +    {
 +        mCurrent = anotherIterator.mCurrent;​
 +        mInterval = anotherIterator.mInterval;​
 +        return *this;
 +    } 
 +    // Opération de permutation. ​
 +    // A la fin de l'​opération, ​
 +    // - anotherIterator fait référence à l'​élément ​
 +    // qui était référencé par l'​itérateur courant avant l'​opération,​
 +    // - l'​itérateur courant ​ fait référence à l'​élément ​
 +    // qui était référencé par l'​itérateur anotherIterator avant l'​opération,​
 +    void swap(interval_iterator&​ anotherIterator)
 +    {
 +        std::​swap(mCurrent,​ anotherIterator.mCurrent);​
 +        std::​swap(mInterval,​ anotherIterator.mInterval);​
 +    }
 +</​code>​ \\ Et normalement l'​ensemble des éléments ont été définis pour à la classe ''​interval_iterator''​ pour que ce dernier puisse répondre aux exigences auxquelles un itérateur de type [[https://​en.cppreference.com/​w/​cpp/​named_req/​ForwardIterator|forward iterator]] doit répondre. ​    
 +
 </​hidden>​ </​hidden>​
  
Line 280: Line 345:
  
 Tester le bon fonctionnement de classe avec le code suivant : Tester le bon fonctionnement de classe avec le code suivant :
 +
 +<code cpp>
 +
 +#​include"​interval.hpp"​
 +#​include<​algorithm>​
 +#​include<​iostream>​
 +#​include<​vector>​
 +
 +template<​class inputIterator>​
 +void print(inputIterator beginIt, inputIterator endIt)
 +{
 +    if(beginIt == endIt)
 +    {
 +        std::cout << "​[]"​ << std::endl;
 +        return;
 +    }    ​
 +    for(;;)
 +    {
 +        std::cout << *beginIt;
 +        beginIt++;
 +        if(beginIt == endIt)
 +            break;
 +        std::cout << ", ";
 +    }
 +    std::cout << "​]"​ << std::endl;
 +    return;
 +}
 +
 +void testInterval()
 +{
 +    interval inter(5, 15);
 +    std::cout << "​Contenu de l'​interval : "; ​
 +    print(inter.begin(),​ inter.end());​
 +    // Recherche du plus grand élément.
 +    auto maxIt = max_element(inter.begin(),​ inter.end());​
 +    std::cout << "​Element maximal de la séquence: " << *maxIt << std::endl;
 +    // Duplication des valeurs dans un vecteur
 +    std::​vector<​int>​ vec(inter.begin(),​ inter.end());​
 +    std::cout << "​Contenu du vecteur : "; ​
 +    print(vec.begin(),​ vec.end());
 +}
 +
 +int main()
 +{
 +    testInterval();​
 +    return 0;
 +}
 +</​code>​
 +
 +<hidden Explications>​
 +<nodisp 2>
 +
 +
 +  * La fonction ''​print''​ prend une séquence d'​éléments définie par un itérateur de début ''​beginIt''​ et un itérateur marquant la fin de la séquence ''​endIt''​ et affiche cette séquence sur la console. La seule particularité,​ c'est qu'en fonction de la position de l'​élément,​ le séparateur ","​ est placé entre les valeurs ou non.
 +
 +  * Dans le cas de la fonction ''​testInterval()'',​ le code <code cpp>
 +    interval inter(5, 15);
 +    </​code>​ crée un interval allant de 5 à 15. Nous appelons la fonction ''​print''​ en passant l'​itérateur référençant la première valeur contenue dans l'​interval ainsi que l'​itérateur marquant la fin de la séquence et nous vérifions que la fonction ''​print''​ peut bien afficher les valeurs contenues dans l'​interval. <code cpp>
 +    print(inter.begin(),​ inter.end());​
 +</​code>​. Nous vérifions en utilisant une fonction [[https://​en.cppreference.com/​w/​cpp/​algorithm/​max_element|max_element]] de la bibliothèque standard qui recherche le plus grand élément d'une séquence que notre containeur fonctionne aussi avec des fonctions de la bibliothèque standard. <code cpp>
 +    auto maxIt = max_element(inter.begin(),​ inter.end());​
 +    std::cout << "​Element maximal de la séquence: " << *maxIt << std::endl;
 +</​code>​Nous affichons l'​élément retourné qui est bien entendu la borne supérieure de l'​interval. Enfin, nous créons un vecteur auquel nous affectons l'​ensemble des valeurs contenues dans l'​interval ''​inter''​. Et nous vérifions que notre fonction ''​print''​ fonctionne aussi avec un containeur de type ''​std::​vector''​.<​code cpp>
 +    // Duplication des valeurs dans un vecteur
 +    std::​vector<​int>​ vec(inter.begin(),​ inter.end());​
 +    std::cout << "​Contenu du vecteur : "; ​
 +    print(vec.begin(),​ vec.end());
 +</​code>​
 +
 +</​nodisp>​
 +</​hidden>​
 +
  
in204/tds/sujets/td4/part2.1571135880.txt.gz · Last modified: 2019/10/15 10:38 by bmonsuez