Types


To translate this course in your native langage:

  • In Chrome: right-click anywhere on the page and select “Translate to…”

  • Or use this language selector:

Les mots colorés en anglais représentent des mots-clés de la norme du langage C++. Ils sont conservés à l’identique lors de l’utilisation d’une traduction automatique avec Google Translate. Exemple : l’héritage (inheritance) est un principe fondamental de C++.


Définition

En C++, pour créer une variable, il faut lui associer un type (type) servant à décrire l’information stockée dans cette variable : nombre, caractère, date…

Le type d’une variable détermine :

  • les valeurs qu’elle peut contenir

  • les opérations autorisées

Exemples d’opérations autorisées sur le type int :

Opération

Exemple

Autorisée

Addition +

3 + 5

Oui

Comparaison <

3 < 5

Oui

Accès à un élément [ ]

a[0]

Non

Le typage statique

Le langage C++ dispose d’un typage statique (statically typed language). Cela signifie que les types de toutes les variables sont déterminés au moment de la compilation, plutôt qu’à l’exécution. Ainsi, le type de chaque variable doit être soit:

  • déclaré explicitement par le programmeur

  • inféré par le compilateur (par exemple le résultat de 5+3 est un entier)

Cela implique qu’aucune variable n’est autorisée à changer de type durant l’exécution du programme, d’où le terme de statique. De cette façon, aucun temps n’est perdu durant l’exécution du programme à effectuer des vérifications sur les types des variables, d’où un gain de performance.

Indiquez si les affirmations suivantes sont vraies ou fausses :

  1. Si le langage utilise le typage statique, les variables ne peuvent pas changer de valeur.

  2. Le typage statique permet de détecter toutes les erreurs dans un programme.

  3. Le typage statique interdit à une variable d’avoir plusieurs types durant l’exécution.

  4. Le typage statique permet de détecter des erreurs entre types incompatibles avant l’exécution du programme.

  5. Le typage statique permet une efficacité accrue des programmes.

Ainsi, l’utilisation des type permet au compilateur de vérifier qu’un programme est cohérent.

Les types fondamentaux

Les types du C++ se divisent en deux catégories :

  • Les types fondamentaux (fundamental types)prédéfinis dans la norme du langage

  • Les types composés (compound types) correspondant à des nouveaux types définis par le programmeur

Les types fondamentaux sont spécifiés par la norme du langage C++ sont intégrés au compilateur. Ils sont ainsi utilisables dans le code, à tout moment, sans avoir à inclure de fichier d’en-tête. Il en existe une vingtaine, mais nous nous limiterons aux suivants :

Quelques types fondamentaux du C++

Catégorie

Mot clé

Valeurs

Booléen

bool

true ou false

Caractère

char

`A` la lettre entre apostrophes (touche 4)

Numérique entier

int

Nombres entiers à 9 chiffres

Numérique flotant

double

Nombre à virgule avec 15 chiffres significatifs

Aucun type

void

Absence de valeur

Remarque : le type void permet d’indiquer qu’une fonction ne retourne pas de valeur, elle retourne le type void. Ainsi, toute fonction a un type de retour !

Conversion des types numériques

La conversion (conversion) consiste à transformer une valeur d’un type donné vers un autre type.

REGLE 1 : Lors d’une affectation, la valeur numérique transmise peut être convertie vers le type de la variable de destination. Cette conversion est valide et implicite.

Avec :

  • valide signifie qu’aucun message d’erreur ne se produit

  • implicite (implicit) signifie que le compilateur effectue la conversion sans que vous rajoutiez une commande dans le code

Voici quelques exemples :

int a = 3.5;          // conversion implicite de double vers int
cout << a;            // ===> 3

double f = 3;         // conversion implicite int vers double
cout << f;            // ===> 3.0000

La conversion implicite peut être à l’origine d’ambiguïté. Prenons l’exemple ci-dessous. Comme la fonction retourne un type int, lorsque l’on retourne une valeur de type double, celle-ci sera convertie implicitement en int et la fonction retournera en fait la valeur 3.

int testf()
{
   return 3.141519;
}

REGLE 2 : Lors d’une opération arithmétique entre un int et un double, le C++ convertit implicitement la valeur de type int vers un double avant d’effecteur l’opération. Le résultat est donc de type double.

De cette façon :

Expression

Conversion effectuée

Type du résultat

int + int

aucune

int

int + double

int → double

double

double + double

aucune

double

Pour chaque initialisation, indiquez si elle déclenche une conversion implicite et la valeur finale (en notant xxx.0 pour les doubles) :

double d = 1.0;

int i = 3.7;

double d = 2;

int i = 1;

int i = 0*sin(u);

Pour chacune des expressions, donnez son type et sa valeur. Tenir compte de la priorité des opérations.

Expression

Type

Valeur

2 + 3 / 2

2 + ( 5 % 2 )

2.0 + 3 / 2

2 + 3.0 / 2

5 <= 3 + 2

1.5 + 2

1.5 + 2.0

Les types composés

Les types composés (compound types) sont construits à partir d’autres types.

Le type fonction

Le type fonction (function type) est défini à partir :

  • du type de chaque paramètre

  • du type de retour

Ainsi dans l’exemple suivant, le nom Test est associé au type fonction prenant deux entiers et retournant un double.

double Test(int a, int b) ...

Le type référence

Une référence crée un alias vers une variable déjà existante, nous en reparlerons plus tard. Le type référence (reference type) se crée en ajoutant un symbole & (et commercial ou ampersand) après un type :

  • int & : référence vers un int

  • double & : référence vers un double

Le type structure

Une structure (structure) permet de regrouper plusieurs paramètres dans une sorte de container :

struct Aventurier
{
    int pv;
    int endurance;
    int piecesOr;
};

Note

Dans le langage C, la syntaxe des struct est différente. La version que nous présentons ici est la version simplifiée propre au C++.

Le type enumeration

Une énumération (enumeration type) permet de définir une liste de valeurs sous forme de mots-clés. Cette approche permet d’améliorer grandement la lisibilité du code.

enum class Feu
{
    Rouge,
    Orange,
    Vert
};

Feu feu1 = Feu::Vert;

Remarque : le mot-clé class n’a ici aucun rapport avec les classes de la POO. Il fait simplement partie de la syntaxe moderne des énumérations en C++.

Pour chacune des affirmations suivantes, indiquez si elles sont vraies ou fausses :

  • En C++, le type fonction est donné uniquement par les paramètres de la fonction

  • « Fonction recevant un entier et retournant un entier » correspond à un type de fonction

  • Le type référence se construit en utilisant un signe *

  • En C++ il existe les types composés et les types fondamentaux

  • Le type struct est un type fondamental

  • Une énumération permet d’éviter des constantes entières sans signification

  • Une structure permet de regrouper plusieurs informations

Donnez le terme C++ signifiant type composé - ne pas utiliser de majuscules :

Nommer des types

Il est possible de donner un nom à un type composé afin de simplifier son utilisation.

SYNTAXE : using nomDuType = Type;

Prenons l’exemple d’un tableau d’entiers de taille 5x5 :

using T55 =  int[5][5];  // T55 est un nouveau nom désignant le type tableau d'entiers de taille 5x5

int main()
{
        T55 T2;              // création d'un tableau d'entiers de taille 5x5
}

Note

Pour la syntaxe, il faut penser à la définition d’une variable du type en question puis écrire cette définition sans donner le nom de la variable. Ainsi, pour créer un tableau T de 10 entiers, la syntaxe est : int T[10]. Ainsi, en retirant le nom T on obtient comme syntaxe pour le type tableau de 10 entiers : int[10].

Pour chacune des affirmations suivantes, indiquez si elles sont vraies ou fausses :

  • Il est possible de nommer un type.

  • La syntaxe : using monint int; est correcte.

Surcharge de fonctions

Prenons un cas pratique : lorsque l’on recherche le minimum entre deux valeurs, il est naturel de vouloir utiliser une fonction min() pouvant gérer des entiers comme des doubles.

On pourrait écrire une fonction min() pour chaque cas : int/int, int/double, double/int et double/double, mais cela conduirait à un grand nombre de fonctions. Finalement, en pratique, on crée généralement une fonction min pour chaque paire de même type :

int    min(int a, int b)       { ... }
double min(double a, double b) { ... }

Le C++ permet de créer plusieurs fonctions de même nom à condition que les types de leurs paramètres soient différents. Ce mécanisme s’appelle la surcharge de fonctions (function overloading). Le type de retour de la fonction n’intervient pas.

Examinons l’appel suivant :

min(3.0, 5);

Aucune des deux fonctions n’accepte un double et un int. Le compilateur doit alors choisir la fonction la plus appropriée. Pour cela, il utilise un mécanisme appelé résolution de surcharge (overload resolution) donc nous résumons la logique ci-dessous :

LOGIQUE :

  • Si une fonction possède exactement les types demandés, elle est appelée.

  • Sinon, le compilateur autorise les conversions implicites.

    • Il recherche alors toutes les fonctions pouvant convenir

      • Il sélectionne la/les meilleures fonctions en tenant compte de plusieurs critères

      • Si une seule fonction est retenue, elle est appelée

      • Sinon, le compilateur signale une erreur

Note

Dans ce cours, nous simplifions la liste des critères et retenons uniquement le suivant : la fonction retenue est celle générant le moins de conversions implicites.

Ainsi, dans l’exemple ci-dessus, l’appel de min(3.0,5) produit une erreur car les deux fonctions min(int,int) et min(double,double) requièrent toutes deux 1 conversion implicite.

Avertissement

Le mécanisme de résolution de surcharge est plus complexe qu’il n’y paraît. Pour déterminer la fonction à appeler, le compilateur utilise un algorithme prenant en compte de nombreux critères : promotions, conversions, héritage, qualificatif const, template, etc… Les règles précises dépassent le cadre de ce cours.

Exercices

  1. La surcharge de fonctions permet d’avoir des fonctions de même nom avec des paramètres de types différents.

  2. Si des fonctions sont disponibles, la résolution de surcharge trouve toujours une fonction adéquate.

  3. La surcharge de fonctions utilise le type de retour pour savoir quelle fonctionner appeler.

  4. La surcharge de fonctions peut déclencher des conversions implicites.

Donnez le terme C++ signifiant surcharge de fonctions - ne pas utiliser de majuscules :

Soit le programme suivant:

1 int    F(int a)               {  return a; }
2 double F(double a)            {  return a; }
3 double F(int i, int j)        {  return i; }
4 double F(double i, int j)     {  return i; }

Pour chaque chaque appel de fonction, indiquez quelle définition de F() est utilisée ou ERR si l’appel n’est pas valide.

Appel de fonction

Définition utilisée

F(1.0)

F(2)

F(2.0,5)

F(1, 2.0)

F(1.0, 2)

F(1.0, 2.0)