Académique Documents
Professionnel Documents
Culture Documents
Si vous souhaitez partager ce livre avec une autre personne, veuillez en acheter
une copie supplémentaire pour chaque destinataire. Merci de respecter le travail
acharné de cet auteur. Dans le cas contraire, la transmission, la duplication ou la
reproduction de l'un ou l'autre des travaux suivants, y compris des
renseignements spécifiques, seront considérés comme un acte illégal, qu'ils
soient effectués par voie électronique ou écrit. Cela s'applique à la création
d'une copie secondaire ou tertiaire de l'œuvre ou d'une copie enregistrée qui
n'est autorisée qu'avec le consentement écrit exprès de l'éditeur. Tout droit
supplémentaire réservé.
TABLE DES MATIÈRES
./main.out
Vous pouvez créez un fichier .cpp dans le bloc note de votre choix.
Lancer C++ en ligne
La manière la plus simple d’utiliser C++ en ligne est d’utiliser
« TutorialPoints » C++ Online, comme ci dessous :
https://www.tutorialspoint.com/compile_cpp11_online.php
Cela devrait ressembler à ça :
Pré-requis
Un certain nombre de choses doivent être éclaircies avant de commencer avec
les basiques.
#include "stdafx.h"
int main()
{
return 0;
}
Le programme ci dessus est le modèle de base proposé, le code s’insère entre
les parenthèses () de la section principale ; chaque aspect sera décrit dans les
sections suivantes.
C++ est un langage lourdement caractérisé, dans lequel chaque variable doit
avoir un type défini. Un type est un identifiant sous la forme d’un mot-clé qui
définis ce que la variable peut contenir. Le premier type auquel nous allons
nous confronter est le nombre entier (ou integer), pouvant contenir seulement
des nombres réels (sans décimales), sa définition étant :
int value = 6;
Cette variable peut désormais être utilisée dans les zones valides du
programme, comme ceci :
int anotherValue = value;
Chaîne (String)
La chaîne est un autre type de variable crucial, utilisé afin de stocker une série
de caractères, comme par exemple « batman » ; le mot est composé de
caractères stockés dans la variable Chaîne. Il s’agit d’un tableau de caractères
(nous reviendrons plus tard sur les tableaux), mais dans les faits ce sont les
caractères seuls du mot stockés les uns à côté des autres dans la mémoire. Le
« \n » est un caractère spécial définissant une nouvelle ligne.
#include "stdafx.h"
#include <string>
int main()
{
//String class
string wordStr = "";
//Character array
char wordCharArray[] = "";
return 0;
}
Comme vous pouvez le voir ci dessus il existe deux façons de sauvegarder une
chaîne/string. La première étant d’utiliser un « wrapper », qui vise à cacher la
fonctionnalité et ajouter des extras, permettant une navigation plus facile autour
d’un string (nous reviendrons sur la navigation autour des variables). La
seconde manière étant d’utiliser un tableau de caractères, string étant le moyen
de stockage des variables string recommandé.
Notez l’utilisation de :
#include <string>
Mots-clés notables/Termes
const - Ce mot-clé rends la variable lisible seulement, c’est à dire que sa valeur
ne pourra être changée après initialisation. Le mot-clé est utilisé comme cela :
Variable globale - Il s’agit là d’un terme décrivant une définition variable hors
de la fonction principale. Par exemple, le int friendCount est une variable
globale et le int currentMonth ne l’est pas. Observez leurs positions :
//Global
int friendCount = 0;
int main()
{
//Not Global
int currentMonth = 5;
return 0;
}
Cela veut dire que la variable globale peut être utilisée n’importe où dans le
programme, et peut devenir dangereuse si elle n’est pas utilisée correctement.
Une façon correcte est de l’utiliser en conjonction avec le mot-clé const
mentionné ci-dessus, ce qui signifie que ses fonctions peuvent uniquement
référencer la valeur et non la modifier.
Récapitulatif
- int → Contient des nombres réels et entiers
- flottant/double → Contient des nombres décimaux
- void → Spécifie qu’il n’y a pas de type
- boolean → Contient vrai ou faux
- string → Tableau de caractères formant un mot ou une phrase
- global → Variable pouvant être utilisée n’importe où
- const → Signifie qu’une variable initialisée ne pourra pas voir sa valeur
modifiée
Conditionnelles
Déclaration « Si »
Il existe des situations dans lesquelles vous voulez qu’une action se déclenche
uniquement sous certaines conditions. C’est le rôle de la déclaration « Si » et le
moment de parler des déclarations conditionnelles.
if (Condition)
{
//Code will run here if Condition is true
}
//Code will jump here if Condition is false
En action :
#include "stdafx.h"
#include <iostream>
int main()
{
int numberOfBooks = 10;
if (numberOfBooks > 0)
{
cout << "You have a book!" << endl;
}
}
Output:
> You have a book!
Ceci permet à un programmeur de contrôler le flux du programme et de choisir
les situations à venir lorsqu’une possibilité est vraie et vérifiée. Nous en verrons
d’autres exemples plus tard.
Déclaration « Autre »
« Autre » est optionnel mais peut être ajouté à la fin d’une déclaration « Si »
afin de lancer le code uniquement si la condition de la déclaration est fausse.
if (Condition)
else
{
Les déclarations Autre peuvent aussi devenir des Autre/Si quand une nouvelle
déclaration Si est ajoutée, comme cela :
if (Condition1)
}
if else(Condition2)
Vous pouvez également lier deux déclarations Autre ensemble, créant alors une
chaîne infinie. Si une condition de la chaîne est vraie, alors celles suivantes ne
seront pas vérifiées :
#include "stdafx.h"
#include <iostream>
int main()
{
if (false)
{
cout << "Contition1!" << endl;
}
else if (true) //This condition is true!
{
cout << "Contition2!" << endl;
}
else if (true) //Ignored, due to previous else statement being true
{
cout << "Contition3!" << endl;
}
else if (true) //Also ignored due to condition 2 being true
{
cout << "Contition4!" << endl;
}
return 0;
}
Output
> Condition 2
Après que le corps de la condition 2 ai été atteint et que le cout ai été exécuté,
le programme ne continuera pas à vérifier les autres déclarations.
EXERCICE
Adaptez un programme pour vérifier si une valeur est égale à une autre, et
imprimez « EQUAL » ; si elles ne le sont pas inscrivez « NOT EQUAL ». Le
code de base est le suivant :
#include "stdafx.h"
#include <iostream>
int main()
{
int value1 = 10;
int value2 = 10;
Solution
#include "stdafx.h"
#include <iostream>
int main()
{
int value1 = 10;
int value2 = 10;
if (value1 == value2)
{
cout << "EQUAL";
}
else
{
cout << "NOT EQUAL";
}
return 0;
}
Changez les valeurs de value1 et value2 et voyez comment le programme
change.
AND vérifie si les deux conditions sont vraies avant de déclencher le corps de
la déclaration.
}
OR vérifie si une des conditions est vraie avant de déclencher le corps de la
déclaration.
if (Condition1 || Condition2)
//Code will run here if Condition1 OR Condition2 are True (Works if both
are true)
}
RÉCAPITULATIF
- Déclaration SI → Traite des déclarations conditionnelles, le code de son
corps se lance lorsque la condition est vraie
- Déclaration Autre → Utilisé en tant qu’extension à une déclaration Si,
vérifiée seulement si la déclaration est fausse
- && → Utilisé pour lier deux conditions, ne redeviendra vrai que si les
deux conditions sont vraies
- ⎢⎢ (Double barre) → Utilisé pour lier deux conditions, ne redeviendra
vrai que si une des conditions sur les deux est vraie
Switch-Case
Les switch case sont utilisées pour tester une variable sur son égalité avec une
expression constante sans avoir besoin de plusieurs déclarations
conditionnelles. Une des utilisations possibles pour cette structure est de vérifier
les d’entrées des utilisateurs. Trouvez ci-dessous la structure de base de la
switch-case :
//Switch-Case
switch (expression)
{
//Case statement
case constant-expression:
break; //Break isn't needed
default:
break;
}
Le switch commence avec une « expression », il s’agit de la variable qui va être
comparée à l’expression constante. Ces constantes sont les valeurs littérales de
la variable, comme « 1 » ou « Z ». Les breaks sont optionnels, mais sans eux le
code risque de découler dans d’autres déclarations. Ci dessous se trouve un
exemple d’utilisation d’une déclaration avec switch-case. L’utilisateur inscrit un
caractère si ce dernier est N ou Y, « No » ou « Yes » sont produits
respectivement mais il y a une case par défaut qui s’applique à toutes les
situations, ceci doit se trouver à la fin.
char character;
//Switch-Case
switch (character)
{
case 'N':
printf("NO\n");
break;
case 'Y':
printf("YES\n");
break;
default:
printf("Do not understand!\n");
break;
}
S’il n’y a aucune déclaration break, une descente va se produire. Voici un
exemple similaire à celui ci-dessus, sans les déclarations break :
char character;
//Switch-Case
switch (character)
{
case 'N':
printf("NO\n");
case 'Y':
printf("YES\n");
default:
printf("Do not understand!\n");
}
Si « N » est l’ajout de l’utilisateur, le résultat sera :
>NO
>YES
Rouge
Il s’agit de la section déclaration, elle permet de définir la variable du compteur
de boucles, le point de départ du compteur.
Vert
Cette section est appelée la Conditionnelle et contient une déclaration
conditionnelle verifiée à la fin de la boucle afin de voir si la déclaration Si doit
continuer en boucle. Dans ce cas précis, la boucle devrait continuer si x > 10, si
cette condition devient fausse alors la boucle s’arrêtera.
Bleu
La partie bleue est la section de l’Incrément, dans laquelle le compteur de
boucles est incrémenté (augmenté en valeur), le x++ est un raccourci pour x = x
+ 1. Cela peut aussi être x - -, si jamais un cas nécessite de diminuer le
compteur.
Boucles conditionnelles
Les boucles conditionnelles fonctionnent comme les boucles mais n’ont pas de
compteur de boucles, elle ne se mettront en marche que si une condition est
vraie. Cela veut dire que vous pouvez créez une boucle infinie, comme ceci :
while (true)
{
cout << "This will never stop looping!" << endl;
}
Note : Une boucle infinie est normalement construite en utilisant un « pour la
boucle » vierge :
for (;;)
{
while (false)
{
cout << "This will never loop!" << endl;
}
Pour utiliser efficacement cette boucle, vous pouvez vous servir d’une
déclaration conditionnelle (comme la déclaration Si) ou l’utiliser avec une
variable bool, voir les exemples ci-dessous.
int count = 0;
while (count > 10)
{
int count = 0;
bool keepLooping = true; //Bool is true (1) is true
while (keepLooping)
{
//Remember this, it’s the same as “count = count + 1;”
count++;
if (count == 3)
{
keepLooping = 0; //keepLooping is now false, and the loop will stop
}
}
Note : La section entre les crochets du « pendant la boucle » vérifie si la
condition est vraie, vous pouvez également l’écrire comme suit :
while (!stopLooping) {}
En d’autres termes cela revient à dire : continuer la boucle tant que stopLooping
est faux (ou plutôt non-vrai), le « ! » est un symbole signifiant « non ».
while (false)
{
cout << "While Loop" << endl;
}
do
{
cout << "Do Loop" << endl;
} while (false)
Output:
> Do Loop
Car même si le Boolean est faux, la boucle « faire » s’exécute une seule fois
parce que la vérification a été faite à la fin du corps.
#include "stdafx.h"
#include <iostream>
int main()
{
//Will loop if this is false
bool correct = true;
do
{
cout << "Please enter the letter 'a':";
Break arrêtera la boucle toute entière, cela peut être utile si une réponse a été
trouvée et que le reste des itérations prévues s’avèrent inutiles.
Output:
Output:
Output:
Cela montre que quand x = 3, le continue est exécuté et la boucle est passée,
tout comme la déclaration cout ; il n’y a alors pas de « Loop value: 3 ».
Boucles imbriquées
Vous pouvez également placer des boucles dans des boucles pour leur attribuer
des actions spécifiques, dans l’exemple nous utilisons des « pour les boucles »
mais cela peut également être fait avec les autres types de boucles que nous
avons vus.
Cet exemple imprime une grille 2D, la boucle imbriquée lui confère une autre
dimension :
Output
>XXXXX
>XXXXX
>XXXXX
>XXXXX
>XXXXX
Fonctions
Les fonctions agissent comme les fondations d’un programme, elles permettent
la réutilisation d’un code, la possibilité de le garder lisible et d’arrêter le
programmeur de répéter le code. Répéter un code est fortement déconseillé
puisque les bogues seront réitérés de multiples fois et les changements de code
auront également besoin d’être répétés. Les fonctions créent une aire de
contrôle centralisée qui gère les différents rôles du programme.
Une fonction doit être définie au-delà de son appel, comme suit :
//Function call
void PrintSmile()
{
cout << ":)" << endl;
}
int main()
{
//Method call
PrintSmile();
return 0;
}
Paramètres des fonctions
Il peut parfois s’avérer utile de transférer ou envoyer des données dans un
programme, il existe deux types de paramètres d’envoi : par référence et par
valeur. Passer par référence, comme cela l’indique, veut dire que l’ont envoie
une référence directe à une variable, pas une copie, donc chaque changement à
l’effet de cette variable envoyée seront répercutés dans la fonction d’appel.
Passer par valeur corresponds à l’envoi d’une copie de cette variable, donc tous
les changements de cette variable n’affecteront pas la variable passée.
- Passer par référence veut dire que les changements de paramètres affectent
la variable envoyée
- Passer par valeur veut dire que les changements de paramètres n’affectent
pas la variable envoyée
//Prints result
printf("The result is: %d", newValue);
}
int main()
{
//Method call
Add(10, 4);
return 0;
}
Output:
Ci-dessous un autre exemple, mais cette fois un String est utilisé comme
paramètre :
PrintStr("Flying Squirrel");
int main()
{
int value = 10;
Change(value);
return 0;
}
Output
>20
Comme vous pouvez le voir, le changement de méthode altère la valeur de la
valeur integer et affecte la valeur dans la méthode principale.
Renvoi de valeurs
Le renvoi nous permet de renvoyer des données depuis une méthode, il nous
laisse faire du calcul au sein d’une fonction et fait en sorte que cette dernière
renvoie automatiquement le résultat. Prenons un des exemples précédents et
adaptons-le pour qu’il renvoie le résultat au lieu de l’imprimer :
//Returned keyword
return newValue;
}
int main()
{
//Method call
int storeResult = Add(10, 4);
return 0;
}
Les zones ayant changé ont été surlignées en vert. Quand une valeur doit être
renvoyée, le mot-clé « return » est utilisé, après que cette ligne ai été lancée il
retourne à la ligne d’appel de la méthode afin que chaque code en dessous le
renvoi ne se lance pas.
Le résultat renvoyé est ensuite conservé dans « storeResult », pour une
utilisation ultérieure. Le renvoi peut se faire avec n’importe quel type de
variable. Voyons un exemple ci-dessous qui vérifie si le nombre est une valeur
paire (en utilisant l’opérateur de module dont nous avons parlé précédemment
qui trouve le reste d’une division) :
int main()
{
if (EvenNumber(2))
{
cout << "Even number!" << endl;
}
if (EvenNumber(5))
{
cout << "Odd number!" << endl;
}
return 0;
}
Output:
>Even number!
Ce programme utilise la variable renvoyée depuis la méthode EvenNumber()
comme une conditionnelle pour la déclaration Si ; et si elle est vraie, il
imprimera « Even number! ». Comme vous pouvez le voir au niveau du
résultat, la première imprime tandis que la seconde non.
Tableaux
Les tableaux ont été mentionnés précédemment ; il s’agit d’une structure de
données contenant un nombre fixe de variables les unes à côté des autres dans
la mémoire. Le tableau se verra attribuer un type, par exemple « int ». Les
tableaux sont utilisés pour définir rapidement de nombreuses variables, et
garder pertinentes les variables ensemble. Un tableau est défini ci-dessous :
int lotsOfNumber[20];
Vous pouvez également définir la valeur à la définition, Note : une taille n’a pas
besoin d’être définie car elle est automatiquement déterminée par le nombre de
valeurs spécifiées :
Les tableaux sont très utiles pour accéder rapidement aux données étroitement
liées, vous pouvez utiliser un « pour la boucle » pour boucler à travers les index
et les utiliser selon. Un exemple ci-dessous :
Output:
>1
>3
>4
> 10
Dans C++ vous ne pouvez pas obtenir la longueur directement, vous aurez
besoin d’y travailler. Cela peut être fait simplement en utilisant le mot-clé
sizeof(), qui renvoie la taille des éléments entre crochets. Donc par exemple sur
une machine 64bit, un int sera représenté en utilisant 4bytes, alors sizeof
renverra 4. La longueur d’un tableau peut être travaillée comme ceci :
Tableau multi-dimensionnels
Vous pouvez également définir une deuxième dimension dans un tableau, voire
même une troisième, cela accordera plus de flexibilité lors des travaux avec
tableaux. Par exemple, un tableau 2D peut être utilisé pour stocker les
coordonnées ou les positions d’une grille. Il est définit comme cela :
int arrayOfNumbers[][][2] =
{
{{1,1},{1,1}},
{{1,1},{1,1}}
};
{
//Length is used to dynamically determine the for loop length
for (int i = 0; i < length; i++)
{
printf("%d\n",ageArray[i]);
}
}
int main()
{
//An array of ages
int ages[] = { 32, 11, 12, 1, 8, 5, 10 };
//Method call
Print_SingleDimenArray(ageLen, ages);
}
Output
>32
>11
>12
>1
>8
>5
>10
Le tableau a été passé et utilisé pour imprimer des valeurs. La longueur du
tableau est cruciale pour le programme, puisqu’elle permet au « pour la
boucle » de travailler pour des tableaux de longueurs différentes.
User I/O
Il existe des situations et des problèmes de programmations qui impliquent
l’utilisation de l’interaction de l’utilisateur ; c’est ici qu’interviennent les
entrées et sorties de données par l’utilisateur, la bibliothèque qui gère ceci est
appelée « iostream » et est inclue dans le projet de cette manière :
#include <iostream>
Impression/Sortie de données
Parfois, l’utilisateur a besoin d’une réplique, ou l’information a besoin d’être
affichée ; c’est là que la console d’impression nous sera utile. Le mot-clé cout
est utilisé pour imprimer des données en dehors de l’écran, c’est ensuite utilisé
avec stream insertion operator désigné par « << » pour indiquer ce qu’il faut
imprimer. Voici un exemple :
Vous pouvez également créer une sortie impliquant une variable ou une autre
chaîne (string), plusieurs opérateurs d’insertion de flux sont utilisés :
int x = 10;
cout << "The value is: " << x << endl;
Output
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
//Reads in name
char name[10];
cin >> name;
//Prints name
cout << name;
}
QUIZ DE FIN DE CHAPITRE
Cette section est destinée à vous maintenir à jour par rapport au contenu vu
précédemment ; vous y trouverez 10 questions auxquelles vous pouvez tenter de
répondre pour tester vos connaissances. Vous trouverez les réponses ci-dessous
dans la section avoisinante.
}
9. Quelles sont les deux valeurs contenues par un boolean?
10. Dans quels cas est utilisé le type « void »?
Réponses
1. Chaînes/String
2. Pour obtenir le modulaire de deux nombres.
3. Si les deux valeurs de chaque côté sont égales.
4. La déclaration sera seulement vraie si les deux conditionnelles sont vraies.
5. Cela incrémente/augmente la valeur de cette même variable.
6. Stream extraction operator.
7. Non, la seule manière de trouver sa taille totale et celle de ses éléments est
d’utiliser l’opérateur sizeof() puis de diviser les deux valeurs.
8. Il s’agit d’un vierge pour boucle, qui bouclera à l’infini.
9. Vrai ou Faux.
10. Il est utilisé pour signifier qu’une fonction ne renvoie pas de valeur.
CHAPITRE 3 : STRUCTURE DE CLASSES ET
POO
class Example
{
public:
Example();
~Example();
};
Il y a quelques morceaux clés à ce propos, la section publique définit comment
se fait l’accès aux méthodes ; mais ne vous en inquiétez pas pour le moment,
nous y reviendrons dans la section d’Encapsulation. Aussi, les fonctions
Example() et ~Example() sont respectivement le constructeur et le destructeur ;
le constructeur se lance quand la classe est créée, et le destructeur, lui, quand la
classe est détruite. Ceci peut être fait manuellement, en appelant la méthode ou
alors le compilateur le fera automatiquement.
Le fichier .cpp ressemble alors à ceci :
#include "Example.h"
Example::Example()
{
}
Example::~Example()
{
}
Tout cela est en fait constitué de références aux classes de l’Exemple. Vous
pouvez implémenter des fonctions ou des variables qui ne sont pas définies par
le fichier d’en-tête, cela n’est cependant pas conseillé.
Donc par exemple, utilisons une version étoffée d’une classe pour montrer
comment elle est utilisée :
class Shape
{
public:
Shape(double len, double wid);
~Shape();
double getArea();
private:
double length;
double width;
double area;
};
#include "Shape.h"
#include <iostream>
using namespace std;
Shape::~Shape()
{
}
double Shape::getArea()
{
return area;
}
Et une instance de forme peut être faite comme ceci :
#include "Shape.h"
Avec notre exemple de forme ci-dessus, nous pouvons désormais créer une
autre classe appelée « Square », mais nous héritons des caractéristiques de la
classe de base « Shape ». Pour des besoins d’affichage, nous mettrons tout le
code dans un seul et même fichier .cpp à partir de maintenant. Voici la classe
Square :
#include "stdafx.h"
#include <iostream>
class Shape
{
public:
double length;
double width;
double area;
Shape(double l, double w)
{
length = l;
width = w;
area = l * w;
}
~Shape()
{
}
double getArea()
{
return area;
}
};
int main()
{
Square s = Square(10, 10);
cout << s.getArea() << endl;
Ouput
> 100
Square a désormais tout ce que Shape a défini en tant que public et protégé
(nous expliquerons pourquoi dans les prochaines sections) ; mais comme
mentionné il s’agit d’un bon moyen de répéter un code déjà créé.
EXERCICE
Ajoutez une classe appelée « Rectangle » et laissez la hériter de la classe Shape.
N’oubliez pas d’inclure un constructeur pour la classe Rectangle.
Solution
}
};
Encapsulation
L’encapsulation correspond à la limitation d’accès à des variables sensibles ou
des méthodes contenues dans une classe, il existe trois différents modificateurs
d’accès :
Public
Protégé
Privé
Cette grille compare où vous pouvez et ne pouvez pas accéder à une variable ou
une méthode selon le modificateur choisi.
Ce qui veut dire que vous pouvez contrôler l’accès aux variables. Mais que se
passe-t-il si une variable est marquée privée, comment peut-on y accéder depuis
l’extérieur? Vous avez utilisé des « getters » et « setters », des méthodes
spécialement conçues pour récupérer et altérer des variables. Il s’agit d’une
bonne pratique pour garder un code protégé, modifions alors la forme afin de le
rendre encore plus sécuritaire.
Shape s = Shape(10,10);
s.length = -1;
Toutefois ce n’est pas ce que nous voulons puisque cela invalide l’information,
vous ne pouvez pas avoir de longueur négative, c’est là que nous allons utiliser
un setter ; la fonction peut contenir du code pour valider (vérifier) la valeur
essayant d’être placée dans la variable de longueur :
class Shape
{
private:
double length;
double width;
double area;
public:
Shape(double l, double w)
{
length = l;
width = w;
area = l * w;
}
~Shape()
{
double getArea()
{
return area;
}
double getLength()
{
return length;
}
double getWidth()
{
return width;
}
}
Comme vous pouvez le voir, toutes les variables ont été marquées en privée et
les getters ont été ajoutés, c’est la façon dont les classes devraient être
structurées.
Polymorphisme
Le mot « polymorphisme » signifie avoir plusieurs formes, et dans le contexte
du POO cela veut dire qu’une méthode peut avoir plusieurs rôles.
class Shape
{
protected:
double length;
double width;
double area;
public:
Shape(double l, double w)
{
length = l;
width = w;
}
~Shape()
{
}
virtual double getArea()
{
return length * width;
}
};
Les méthodes virtuelles permettent d’être substituées et changées par une classe
qui en hérite, il s’agit également d’un changement facultatif.
Ouput
EXERCICE
Ajoutez une nouvelle classe appelée Cercle, elle aura besoin d’un constructeur
différent qui prends seulement la hauteur, et la méthode getArea devra être
surchargée.
SOLUTION
class Circle : public Shape
{
public:
Circle(double h) : Shape(h, 0)
{
Le polymorphisme, s’il est correctement utilisé, peut donner une base de code
extrêmement propre et lisible, avec des méthodes et des classes qui s’adaptent si
nécessaire.
Interfaces
Une interface décrit le comportement des capacités d’une classe C++ sans
s’engager à implémentation. Les interfaces sont implémentées en utilisant des
classes abstraites, et sont juste un plan pour la classe.
Cela veut dire que le rôle de cette fonction devra simplement être hérité et
adapté. Ne pas réussir à substituer une fonction virtuelle dans une classe héritée
fait apparaître une erreur, c’est donc un modèle stricte pour concevoir des
classes.
#include "stdafx.h"
#include <iostream>
class Animal
{
public:
//Pure virtual function
virtual void MakeSound() = 0;
};
int main()
{
Dog d;
d.MakeSound();
Cat c;
c.MakeSound();
Lion l;
l.MakeSound();
}
Output
>Bark!
>Meow!
>Raw!
#include "stdafx.h"
#include <iostream>
class Shape
{
public:
//Pure virtual function
virtual int Area() = 0;
Shape(double h, double w)
{
heigth = h;
width = w;
}
protected:
double heigth;
double width;
};
int Area()
{
return heigth * width;
}
};
int main()
{
Rectangle r = Rectangle(10, 20);
cout << r.Area() << endl;
}
Un autre exemple très simple, mais prenez votre temps pour vous y habituer :
l’architecture abstraite de classe, permettant une addition facile d’une nouvelle
fonction et de classes, même si le modèle a été créé grâce au plan facile du
modèle.
QUIZ DE FIN DE CHAPITRE
1. Quels sont les deux fichiers utilisés pour créer une classe?
2. Quel symbole vient avant la définition du destructeur?
3. Quel rôle devrait avoir un fichier d’en-tête?
4. Que veut dire « protégé » pour une variable ayant été héritée?
5. Quel est le rôle du polymorphisme?
6. Quel mot-clé est utilisé pour changer une méthode virtuelle?
7. Comment signifier une fonction virtuelle pure?
8. Où peut-on accéder à une fonction ou une variable marquée en privée?
9. Quel est le rôle du constructeur?
10. Que devrait contenir le fichier .cpp?
RÉPONSES
1. Header (.h) et le fichier C++ (.cpp)
2. Le symbole « ~ » utilisé pour montrer un destructeur
3. Seulement définir les fonctions et les variables
4. Elle peut seulement être accessible en dehors de la définition classe par
classe qui en hérite
5. Il permet d’avoir une seule définition de base mais plusieurs
implémentations différentes
6. La substitution (override) est utilisé pour changer une méthode virtuelle
7. Avec une définition de fonction normale qui est égale à zéro
8. Seulement dans la classe ou elle a été définie
9. C’est une fonction spéciale sans retour type qui est appelée quand un
exemple de classe est créé
10. L’implication des fonctions et variables définies dans le fichier d’en-
tête
CHAPITRE 4 : TECHNIQUES AMÉLIORÉES
Structures
Les structures (structs) sont originaires de C, et viennent du monde de la
programmation antérieur aux classes. Toutefois, elles sont toujours utiles et
peuvent être utilisées lorsque les classes sont réputées trop puissantes.
- le nom
- le numéro
- le salaire
- le pied fort
- etc.
S’il n’y a pas besoin d’ajouter de fonctionnalités dans cette information, une
structure est parfaite pour le cas présente. Une structure stockant ces
informations est définie comme ceci :
struct FootballPlayer
{
string Name;
int KitNumber;
double Wage;
string StrongestFoot;
};
Cette structure contient les valeurs pertinentes, elle va donc fournir un
contenant adéquat pour de l’information similaire.
Un nouvel exemple de cette structure serait celui-ci :
FootballPlayer player1;
player1.Name = "Messi";
player1.KitNumber = 10;
player1.Wage = 1000000;
player1.StrongestFoot = "Both";
Ce code va créer un FootballPlayer et assigner des valeurs à toutes ses
variables.
L’usage réel des structure est souvent très simple, la plupart du temps on
cherche à stocker des données relativement triviales comme celles ci-dessus ;
lorsque vous avez besoin de fonctionnalité et d’encapsulation utilisez une
classe. Ceci s’appelle une encapsulation POD (Plain Old Data).
Note : L’accès par défaut des variables d’une structure est public tandis que
pour les classes le réglage par défaut est privé.
Enums
Enums est le raccourci pour le type Énumérations, qui a le rôle d’un boolean
mais avec un nombre illimité de types définis par l’utilisateur ; cela peut-être
utilisé comme des drapeaux pour des situations comme « HOME_SCREEN »
ou « SETTING_SCREEN ».
enum CurrentScreen
{
HOME,
SETTINGS,
CONTACTS,
CALCULATOR
};
Chaque mot entre crochets est un état que peut revêtir une enum. Un exemple
d’enum pourrait alors être :
int main()
{
CurrentScreen phone1;
phone1 = HOME;
}
Le code ci-dessus crée une nouvelle enum et l’assigne en tant que « HOME ».
Vous pouvez désormais utiliser cette enum pour vérifier quel est son état, par
exemple ci-dessous nous utiliserons une switch-case :
#include "stdafx.h"
#include <iostream>
enum CurrentScreen
{
HOME,
SETTINGS,
CONTACTS,
CALCULATOR
};
int main()
{
CurrentScreen phone1;
//Change me
phone1 = HOME;
switch (phone1)
{
case HOME:
cout << "You're on the home page" << endl;
break;
case SETTINGS:
cout << "You're on the settings page" << endl;
break;
case CONTACTS:
cout << "You're on the contacts page" << endl;
break;
case CALCULATOR:
cout << "You're on the calculator page" << endl;
break;
default:
break;
}
Output
Les unions doivent être utilisées avec précaution puisque définir la valeur d’une
autre variable effacera les données stockées dans une autre, il est donc très
facile de perdre des données importantes.
union Example
{
int i;
int x;
int y;
};
Et un exemple et une valeur sont créés comme ceci :
Example e;
e.i = 4;
#include "stdafx.h"
#include <iostream>
using namespace std;
union Example
{
int i;
int x;
int y;
};
int main()
{
Example e;
e.i = 4;
cout << "Your int size is: " << sizeof(int) << " bytes" << endl;
cout << "The union has 3 variables that should be a total of: " <<
sizeof(int) * 3 << " bytes"<< endl;
cout << "However, the union is " << sizeof(e) << " bytes" << endl;
}
En fonction de l’architecture de votre CPU, le résultat sera :
Ouput
Ci-dessous un code qui montre ce qui arrive à toutes les valeurs lorsqu’une est
changée :
#include "stdafx.h"
#include <iostream>
union Example
{
int i;
int x;
int y;
};
void Print_Union(Example e)
{
cout << "i: " << e.i << endl;
cout << "x: " << e.x << endl;
cout << "y: " << e.y << endl;
}
int main()
{
Example e;
>1
> i: 4
> x: 4
> y: 4
>2
> i: 3
> x: 3
> y: 3
>3
> i: 1
> x: 1
> y: 1
Vous voyez qu’en changeant une valeur, elles sont toutes égales à la même
valeur ; c’est pourquoi toutes les valeurs partagent le même emplacement de
mémoire donc en en changeant une on les modifie toutes.
Les variables sont stockées dans une variable va_list, qui fonctionne comme
n’importe quelle autre variable mais contient seulement la liste des variables
passées. Une fonction variadique est définie ainsi :
Nous pouvons maintenant utiliser ces fonctions pour créer une fonction
variadique fonctionnelle :
#include "stdafx.h"
#include <cstdarg>
#include <iostream>
int total = 0;
for (int i = 0; i < numberOfVariables; i++)
{
//Grabs the next variable
total += va_arg(arg, int);
}
//Clean up
va_end(arg);
cout << "Total is: " << total << endl;
}
int main()
{
//4 Arguments
Add(4,
1, 2, 3, 4);
//10 Arguments
Add(10,
6, 9, 5, 11, 22, 1, 3, 56, 90, 63);
}
Ouput
>Total is: 10
>Total is: 266
#include <cstdarg>
SOLUTION
#include "stdafx.h"
#include <cstdarg>
#include <iostream>
int total = 1;
for (int i = 0; i < numberOfVariables; i++)
{
//Grabs the next variable
total *= va_arg(arg, int);
}
va_end(arg);
return total;
}
int main()
{
cout << "The total is: " << Multi(3, 56, 90, 63) << endl;
}
Espaces de nommage
Les espaces de nommage sont utilisés pour prévenir les variables et fonctions
du même nom de se mélanger. Par exemple, la fonction Run() peut être
appliquée dans de nombreuses situations, elle doit donc être distinguée de
toutes les autres fonctions Run(), c’est là qu’interviennent les espaces de
nommage.
Ils sont définis comme ceci, avec toutes les fonctions et les variables dans les
crochets en tant que partie inhérente de ces espaces de nommage :
namespace NamespaceName
{
}
L’exemple ci-dessous montre comment ils sont utilisés. Deux espaces de
nommage sont définis ainsi avec le même nom de fonction :
namespace Proccesing
{
void Run()
{
cout << "Processing!" << endl;
}
}
namespace Initialisation
{
void Run()
{
cout << "Initialisation started!" << endl;
}
}
Cela donne au programmeur l’opportunité de choisir quel Run() il peut exécuter
juste en spécifiant l’espace de nommage, comme ceci :
Proccesing::Run();
et
Initialisation::Run();
int main()
{
Initialisation::Run();
Proccesing::Run();
}
Output
> Processing!
Les espaces de nommage donnent toutefois un moyen de segmenter nettement
un code, et améliorent la lisibilité en donnant une façon de labellisé des
morceaux de code en quelques sortes.
QUIZ DE FIN DE CHAPITRE
RÉPONSES
#include <fstream>
Vous aurez aussi besoin de « iostream », que nous avons vu précédemment avec
« cout » et « cin ».
#include <iostream>
- ofstream → celui-ci traite avec le flux de sortie et est utilisé pour créer et
écrire des données dans un fichier
- ifstream → celui-là est l’opposé de ofstream et est utilisé pour lire des
données depuis les fichiers
- fstream → cette fonction a les particularités de ofstream et ifstream
simultanément
Ouvrir un fichier
Pour ouvrir un fichier vous devrez utiliser la fonction « open() », membre de
tous les objets principaux, fonctionnant comme suit :
fstream f;
f.open("file.txt", ios::in);
Où :
Name Description
ios::app Ouvre le fichier dans le mode
ajouter, chaque entrée de donnée
sera ajoutée à la fin de fichier
ios::ate Ouvre un fichier à entrer dans un
fichier, et déplace le contrôle lire/
écrire à la fin du fichier
ios::in Ouvre un fichier pour la lecture
ios::out Ouvre un fichier pour l’écriture
Ios::trunc Si le fichier existe, les données
contenues sont effacées (tronquées)
Fermer un fichier
Fermer un fichier est aussi simple que vous le pensez. Pour se faire, tout ce dont
vous avez besoin c’est d’appeler la fonction « close() » :
f.close();
Comme ceci :
char data[100];
f >> data;
Note : Lire comme ceci vous amène à lire toutes les données jusqu’à un espace
ou une nouvelle ligne.
Exemple
Ci-dessus se trouve tout ce dont vous avez besoin pour lire ou écrire dans un
fichier, ci-dessous un exemple concret de code avec lecture et écriture :
#include "stdafx.h"
#include <iostream>
#include <fstream>
int main()
{
//Opens a file for writing
fstream write;
write.open("file.txt", ios::out);
//Prints
cout << data << endl;
}
read.close();
}
Output
>Example
>Data
>More
>example
>data
Comme vous pouvez le voir, chaque commande de lecture est arrêtée quand elle
atteint un espace ou une nouvelle ligne (comme dit précédemment), dans ce cas
là cela complique la section de lecture car nous avons désormais toutes les
sentences qui sont séparées. Il existe cependant une façon de régler ce problème
en utilisant « getline() ». Nous adapterons l’exemple ci-dessus pour ligne les
lignes entières (les changements ont été surlignées) :
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
int main()
{
//Opens a file for writing
fstream write;
write.open("file.txt", ios::out);
//Writes data to file
write << "Example Data" << endl;
write << "More example data" << endl;
write.close();
//Prints
cout << data << endl;
}
read.close();
}
Output
>Example Data
>More example data
La récursion peut être très utile dans la mesure où elle crée une itération avec
une petite base de code, en voici un exemple :
Recursive(5);
Donnera :
>5
>4
>3
>2
>1
Le bit clé de cette fonction ci-dessus est la déclaration Si, permettant de
s’assurer que l’appel récursif s’arrête ; il s’agit de la condition d’arrêt qui stoppe
le programme de crasher à cause d’un trop-plein d’appels de la fonction.
Un exemple plus utile que vous rencontrerez souvent si vous recherchez la
récursion est l’exemple factoriel, qui est le produit de tous les integers positifs
allant de 1 jusqu’à un nombre donné, alors le factoriel de 4 (écrit 4!) est :
4! = 4 * 3 * 2 * 1 = 24
Fibonacci(0, 1);
Note : Vous aurez besoin de deux paramètres, retour/précédent et suivant.
Solution
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
Fibonacci(0, 1);
}
Output
>0
>1
>1
>2
>3
>5
>8
>13
>21
>34
>55
>89
>144
>233
>377
>610
>987
Rappelez-vous qu’il y a plein de manières différentes de faire les choses, alors
si votre cheminement est différent mais que votre résultat de sortie est le même,
c’est correct !
Pré-processeurs
Les pré-processeurs sont des instructions pour le compilateur, ce dernier doit les
traiter avant que tout code soit compilé. Ils commencent tous avec (#) mais ne
finissent pas avec un point virgule (;) puisque ce ne sont pas des déclarations.
#define
Define est utilisé pour créer une définition d’un symbole ou d’un mot ;
l’exemple le plus commun étant d’utiliser #define pour spécifier si le
programme est en mode sans échec. Cela vous permet de lancer seulement le
code de débogue, une fois que le drapeau sans échec est défini. Vous pouvez
également l’utiliser pour définir des valeurs constantes ; en voici un exemple :
#include "stdafx.h"
#include <iostream>
#define NUMBER 10
int main()
{
cout << NUMBER << endl;
}
Output
>10
Ceci est utilisé comme un système alias, aucune variable n’est définie ici mais
vous pouvez ajouter des valeurs importantes dans ces déclarations define, et
créer un code plus lisible.
Vous pouvez également utiliser les déclarations #define pour créer des macro-
fonctions, comme suit :
Cela revient à dire que si b est supérieur à (<) a, il faut retourner (?) b puisque
la valeur autre (:) retourne a.
Les macro-fonctions sont appelées comme toutes les autres fonctions :
int i;
For(i, 10)
{
cout << i << endl;
}
Bien meilleur que son équivalent :
Compilation conditionnelle
Ceci peut être utilisé conjointement à #define, il y a #ifdef (if defined, si défini)
et #ifudef (if undefined, si indéfini) qui vérifient tous les deux les déclarations
#define. Ils peuvent être utilisés ainsi :
#ifdef VERBOSE
cout << "Print!" << endl;
#endif
Cela entour le code et dit par exemple que s’il n’y a pas de #define avant, le
code sera ignoré.
#define VERBOSE;
Les opérateurs # et ##
L’opérateur # replace un macro avec une chaîne donnée, en voici un exemple :
#include "stdafx.h"
#include <iostream>
#define CREATE_STRING(x) #x
int main()
{
cout << CREATE_STRING(STRING EXAMPLE) << endl;
}
Output
>STRING EXAMPLE
Car :
CREATE_STRING(STRING EXAMPLE)
Appelle :
#define CREATE_STRING(x) #x
Là où :
#STRING EXAMPLE
Sont égaux à :
"STRING EXAMPLE"
Opérateur ##
L’opérateur double dièse concatène deux objets en un, cela est mieux expliqué
avec un exemple :
#include "stdafx.h"
#include <iostream>
#define MAKE_ONE(a, b) a ## b
int main()
{
int numone = 100;
Output
>100
Que s’est-il produit? Voyons de plus près.
MAKE_ONE(num, one)
Se résous :
Et :
num ## one
Est égal à :
numone
Donc :
Est égal à :
int* p;
Vous devez ensuite faire en sorte que le pointeur pointe un espace de mémoire
réel, pour se faire il faut utiliser l’opérateur de référence (&). Le code ci-
dessous assigne une variable à pointer au pointeur :
#include "stdafx.h"
#include <iostream>
int main()
{
int i = 10;
int* p = &i;
}
Pour définir un pointeur à un integer, imprimons la valeur juste après
l’initialisation et voyons à quoi cela ressemble, et ce en ajoutant le code ci-
dessous à la fin du corps de la méthode principale :
cout << p << endl;
Output
> 008FF768
Voici l’adresse contenant l’integer « I ».
Cela vous permet de passer les valeurs par référence ; si vous passiez le
pointeur en un paramètre, chaque changement accordé au pointeur résulterait en
un changement à la variable de base, l’exemple ci-dessous le démontre :
#include "stdafx.h"
#include <iostream>
int main()
{
int i = 10;
int* p = &i;
Output
>Before: 10
>After: 200
Comme vous pouvez le voir, la valeur de « I » a changé, sans pour autant
apporter de changement directement à la variable.
Les pointeurs peuvent pointer n’importe quel type de donné, vous pouvez
également pointer des objets.
#include "stdafx.h"
#include <iostream>
int main()
{
//Creates pointer
double* p = NULL;
//Deallocates memory
delete p;
}
Comme vous le voyez, il y a une vérification pour l’attribution de la mémoire
pour p ; il s’agit d’une pratique recommandée car cela permet de garder un oeil
sur les problèmes de mémoire :
double* p;
p = new double;
Si la deuxième ligne échoue, p pourrait pointer n’importe quel espace de
mémoire, ce qui provoquerait un crash si la valeur de p a été changée.
#include "stdafx.h"
#include <iostream>
class MyClass
{
public:
MyClass()
{
cout << "Made!" << endl;
}
~MyClass()
{
cout << "Deleted!" << endl;
}
};
int main()
{
//Create
MyClass* classes = new MyClass[5];
//Delete
delete[] classes;
}
Output
>Made!
>Made!
>Made!
>Made!
>Made!
>Deleted!
>Deleted!
>Deleted!
>Deleted!
>Deleted!
En voici un exemple :
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
//A size check
cout << "int is: " << sizeof(int) << " bytes" << endl;
//Creates memory
int* next = new int[10];
//Deletes memory
delete[] next;
}
Output
>00DDDB54
>00DDDB58
>00DDDB5C
>00DDDB60
>00DDDB64
>00DDDB68
>00DDDB6C
>00DDDB70
>00DDDB74
Comme vous le voyez, la mémoire est attribuée et le pointeur est déplacé au fur
et à mesure de 1 à chaque fois. Dans le résultat, les adresses hexadécimales sont
différentes de 4 entre elles puisque les adresses des espaces de mémoire sont
adjacents.
QUIZ DE FIN DE CHAPITRE
RÉPONSES