TD 11 C++: Fonctions abstraites, exceptions, template<>

Rappels


Exercice 1: Fonctions abstraites (dans le TD10, mais à faire si vous n'aviez pas terminé)

  1. Téléchargez 1.1.h (copié ci-dessous) et complétez-le, et implémentez les classes dans un fichier 1.1.cc.
    class Sequence {
     public:
      // The value of the sequence at the current step.
      double Value();
    
      // Advances to the next step.
      void Next();
    };
    
    // 1, 1, 2, 3, 5, 8, ...
    class FibonacciSequence : public Sequence {
    };
    
    // 1, 1, 2, 6, 24, 120, 720, ... modulo x: if x = 10, that's
    // 1, 1, 2, 6, 4, 0, 0, ...
    class FactorialModXSequence : public Sequence {
     public:
      explicit FactorialModXSequence(int x);
    };
    Veillez à rendre les fonctions de la classe de base Sequence abstraites.

    Testez votre code avec 1.tar.gz:
    rm 1.tar.gz; wget --no-cache http://fabien.viger.free.fr/cpp/td11/1.tar.gz
    tar xf 1.tar.gz
    make 1.1
    RENDU: 1.1.h et 1.1.cc

  2. Copiez 1.1.h et 1.1.cc dans 1.2.h et 1.2.cc.
    Ensuite, Créez une classe ShiftSequence, sous-classe de Sequence, dont le constructeur prendra en argument une Sequence* arbitraire seq et un décalage (entier positif) shift, telle que:
    ShiftSequence(seq, shift)n = seqseq0 + n + shift, où seq0 est l'étape initiale de seq quand on a construit l'objet ShiftSequence.

    Le passage de seq se fera par pointeur, et la classe ShiftSequence prendra possession de l'objet seq donné: c'est lui qui devra se charger de sa suppression.

    Subtilité supplémentaire: contrairement au reste des Sequence donnés jusqu'à présent, vos objets ShiftSequence ne peuvent pas être copiés "naturellement" sans créer des problèmes. Empêchez la copie d'un ShiftSequence vers un autre! Ma solution préférée est la 3ème méthode citée ici.

    Testez votre code avec
    make 1.2
    RENDU: 1.2.h et 1.2.cc

  3. Copiez 1.2.h et 1.2.cc dans 1.3.h et 1.3.cc.
    Ensuite, ajoutez une fonction int Step() qui renvoie l'étape courante d'une Sequence (elle renverra 0 au début). Pour ne pas avoir à la redéfinir pour chaque sous-classe de Sequence, on va utiliser une astuce:
    Testez votre code avec
    make 1.3
    RENDU: 1.3.h et 1.3.cc

Exercice 2: Exceptions

  1. Dans un fichier 2.1.cc, implémentez la fonction ParseInt() décrite dans 2.1.h (également copié ci-dessous), et faites-lui renvoyer les exceptions dédiées, afin que cela fonctionne (avec l'archive 2.tar.gz):
    rm 2.tar.gz; wget --no-cache http://fabien.viger.free.fr/cpp/td11/2.tar.gz
    tar xf 2.tar.gz
    make 2.1
    Ne changez pas 2.1.h!
    #include <exception>
    
    // Converts a string to an integer, with strict checking: throws an exception
    // if 'str' doesn't represent an integer in the range [-2^31...2^31-1].
    //
    // Exceptions thrown:
    // - NullPtrException: if str == nullptr.
    // - EmptyStrException: if str is empty.
    // - BadFormatException: if the string doesn't follow the regexp 0|(-?[1-9][0-9]*)
    // - OverflowException: if the string follows the regexp above, but the integer
    //   is outside p[-2^31...2^31-1].
    //
    // The last two exceptions take an argument at construction: a string that helps
    // to know what failed. Typically it would be the input that's being parsed.
    // The first two don't need it, because the context is pretty clear (nullptr
    // or empty string).
    int ParseInt(const char* str);
    
    using std::exception;
    class NullPtrException : public exception {};
    class EmptyStrException : public exception {};
    class BadFormatException : public exception {
     public:
      explicit BadFormatException(const char* data) : data_(data) {}
      // See http://www.cplusplus.com/reference/exception/exception/what/
      const char* what() const throw() override { return data_; }
     private:
      const char* data_;
    };
    class OverflowException : public exception {
     public:
      explicit OverflowException(const char* data) : data_(data) {}
      const char* what() const throw() override { return data_; }
     private:
      const char* data_;
    };
    RENDU: 2.1.cc

Exercice 3: Fonction générique (aka template)

  1. Implémentez dans un fichier 3.1.h une fonction générique void Print(x) qui prend un argument x et l'imprime (avec cout << x). L'argument x pourra avoir n'importe quel type.
    Notez bien que je demande un .h et pas de .cc! C'est dû à l'utilisation de template: dans un projet C++ classique, on implémente les fonctions génériques dans les .h, pour qu'elle soient utilisables par les autres fichiers (explication au tableau si vous voulez).

    Testez votre code:
    rm 3.tar.gz; wget --no-cache http://fabien.viger.free.fr/cpp/td11/3.tar.gz
    tar xf 3.tar.gz
    make 3.1
    RENDU: 3.1.h

  2. Copiez 3.1.h dans 3.2.h et spécialisez la fonction Print pour qu'elle ait un comportement particulier quand x est un int: elle devra alors afficher "int(x)" (sans les guillemets, et en remplaçant le "x" par sa valeur).
    Vous utiliserez les template, et pas la surcharge!

    Test: make 3.2
    RENDU: 3.2.h

  3. Dans une copie 3.3.h, continuez à spécialiser Print() pour que:
    Test: make 3.3
    RENDU: 3.3.h