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
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 :
Si le langage utilise le typage statique, les variables ne peuvent pas changer de valeur.
Le typage statique permet de détecter toutes les erreurs dans un programme.
Le typage statique interdit à une variable d’avoir plusieurs types durant l’exécution.
Le typage statique permet de détecter des erreurs entre types incompatibles avant l’exécution du programme.
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 :
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
La surcharge de fonctions permet d’avoir des fonctions de même nom avec des paramètres de types différents.
Si des fonctions sont disponibles, la résolution de surcharge trouve toujours une fonction adéquate.
La surcharge de fonctions utilise le type de retour pour savoir quelle fonctionner appeler.
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) |