M2103-TP7-Exo-1

Dans certains cas nous allons avoir affaire à des classes génériques dérivées d’un paramètre de généricité.

Exemple :

template <class T>
class CX : public T
{

};//

Les spécifications de telles classes sont évidemment que le paramètre de généricité soit une classe dérivable, ce qui interdit tous les types de base du C : ints ou floats par exemple.

Cet inconvénient peut être levé si c’est vraiment nécessaire, en encapsulant les types de base du C (int, char, double, etc.) dans de véritables classes, comme le fait le langage Java.

Le mécanisme étant identique pour tous les types de base, il doit être conçu générique dès le début.

Travail à effectuer

Créer le projet CTypeBase.

Dans le fichier TypesBase.hpp, écrire la classe générique TypeBase dans l’espace de noms std.

Lui ajouter :

  • une donnée membre myVal du type générique T,
  • un constructeur explicit ayant un paramètre de type T avec une valeur par défaut,
  • un opérateur de conversion dans le type T.

Parce que les corps des fonctions sont particulièrement simples, ils seront exceptionnellement mis directement à l’intérieur de la déclaration de la classe.

A la suite de la déclaration de la classe TypeBase, on peut l’instancier avec différents types de base au moyen de l’instruction typedef.
Il est possible de profiter de l’occasion pour rendre les types de base ainsi encapsulés indépendants de l’implémentation.
Rappelons en effet que les implémentations des types de base du C/C++ sont variables : un int peut être codé sur 2 ou 4 octets par exemple, ce qui peut poser quelques problèmes de portabilité ou au moins froisser certaines susceptibilités …
Il suffit de chercher une fois pour toutes dans les nombreux fichiers des bibliothèques fournies avec le compilateur C++, à quel endroit est défini un type “entier sur 32 bits” par exemple.
Ici, il s’agit du fichier <cstdint>, qui définit entre autres les types int8_t, int16_t, int32_t et int64_t, ainsi que leur version non signée.
Ajouter les instanciations des types Short, Integer et Character respectivement int16_t, int32_t et uint8_t au fichier TypeBase.hpp.

L’idéal serait de pouvoir se servir de ces nouveaux types en lieu et place des types de base int et short.

Par exemple, on devrait pouvoir écrire la séquence suivante :

for (Integer i; cin >> i; ) cout << i << "; ";

L’injecteur ne pose aucun problème car le compilateur, n’ayant pas de surcharge de l’opérateur << qui accepte un Integer en second paramètre, cherche à convertir le type Integer en un des types pour lesquels il connaît une surcharge.

Ici, il s’agit du type int, grâce à l’opérateur demandé ci-dessus.

En revanche, l’extracteur ne peut fonctionner comme on l’espère : il attend comme second paramètre une référence d’objet, dans lequel il peut transférer la valeur lue dans le flux.

Or l’opérateur demandé ci-dessus renvoie un int, c’est-à-dire une valeur numérique dans laquelle on ne peut évidemment pas mettre une autre valeur.

La solution est donc que l’opérateur de conversion renvoie une référence d’entier (l’adresse de la donnée membre) dans laquelle l’extracteur rangera la valeur lue.

Copier le fichier de test TestTypesBase.cpp dans votre fichier main.cpp

/**
 *
 * @file     TestTypesBase.cpp
 *
 * @authors  M. Laporte
 *
 * @date     07/05/2018
 *
 * @version  V2.0
 *
 **/
#include <iostream>
#include <cassert>

#include "TypesBase.hpp"        // Character

using namespace std;
#define classdef typedef

namespace 
{
    void testTypesBase (void)
    {
        cout << "TestTypesBase : ";

        // Verification de l'arithmetique des entiers avec la classe
        //   Short

        Short s1 (12), s2 (34);
        assert (s1 == 12);
        assert (s2 == 34);
        assert ((s1 + s2) == 46);
        assert (s1++ == 12);
        assert (++s1 == 14);

        cout << "OK\n";

        // Verification du fonctionnement de l'injecteur
        
        cout << "Saisir un Short : ";
        cin >> s1;
        
        cout << "s1 = " << s1 << endl;
        
    }// testTypesBase ()

} // namespace

int main ()
{
    testTypesBase();    // Attention : exception bad_alloc possible ...

    return 0;

} // main()

Compiler et tester.