PLAN DES COURS
VALIDATION
Pour valider le module :
Contrôle des connaissances :
OBJECTIFS
Une introduction au langage C++ ainsi qu’au paradigme objet.
L’objectif est de faire découvrir le langage, d’être capable d’écrire et de concevoir un programme C++ simple de bout en bout.
BIBLIOGRAPHIE
PETITE HISTOIRE DU C/C++
Le C a été inventé au cours de l’année 1972 dans les laboratoires Bell par Dennis Ritchie et Ken Thompson.
En 1978, Brian Kernighan, qui aida à populariser le C, publia le livre « The C programming Language », le K&R, qui décrit le C « traditionnel » ou C ANSI.
Ken Thompson (à gauche) et Dennis Ritchie (à droite).
(source Wikipédia)
PETITE HISTOIRE DU C/C++
Dans les années 80, Bjarne Stroustrup développa le C++ afin d’améliorer le C, en lui ajoutant des « classes ». Le premier nom de ce langage fut d’ailleurs « C with classes ».
Ce fut en 1998 que le C++ fut normalisé pour la première fois. Une autre norme corrigée fut adoptée en 2003.
Une mise à jour importante fut C++11, suivie de C++14, ajoutant de nombreuses fonctionnalités au langage.
Toutes ces normes permettent une écriture indépendante du compilateur. Le C++ est le même partout, pourvu qu’on respecte ces normes.
Bjarne Stroustrup (source Wikipédia)
ASPECT IMPÉRATIF DU C++
Le C++ est une surcouche de C, avec quelques incompatibilités de syntaxe
Un programme C est la plupart du temps un programme C++.
Donc on peut faire du « C+ » c’est-à-dire du C++ sans objet.
Impératifs : les instructions se suivent dans un ordre précis et transmis au processeur de la machine dans cet ordre.
Impératif et objet ne se contredisent pas, C++ est un langage multiparadigmes.
Il respecte à la fois le paradigme objet et impératif.
On va donc commencer par faire du C++ impératif.
HELLOWORLD.CPP
Comme dans la plupart des cours de programmation, on commence par un HelloWorld : Programme le plus simple qui affiche un message à l’écran.
Dans un éditeur de texte quelconque. (notez ici la coloration syntaxique)
Master IM / C++
#include <iostream>
int main()
{
std::cout << "Hello World" << std::endl;
}
COMPILATION ET EXÉCUTION DU
HELLOWORLD
$ g++ HelloWorld.cpp -o HelloWorld
$ ./HelloWorld.exe
Hello World Master IM / C++
LE HELLOWORLD LIGNE À LIGNE
#include <iostream>
En C++ comme en C, les lignes commençant par # sont des « directives préprocesseurs ». Elles s’adressent à un programme appelé préprocesseur, cpp (pour « c preprocessor ») qui prépare le code source en traitant ces directives.
Ici, en appelant #include, on dit au préprocesseur d’inclure le fichier iostream, de la bibliothèque standard C++ et qui contient les définitions pour afficher quelque chose à l’écran via des « flots ».
Master IM / C++
LE HELLOWORLD LIGNE À LIGNE int main()
Il s’agit de l’entête de la fonction main. En C++, une fonction se divise en deux parties principales. L’entête et le corps de la fonction.
On peut voir ici trois éléments fondamentaux dans l’écriture d’une fonction. main est le nom de la fonction. En C++, c’est aussi le point d’entrée du programme.
Nous verrons plus tard ce que cela signifie. Il faut juste retenir que main est la seule fonction qui doit absolument apparaitre dans un programme. C’est une convention.
int est le type de retour de la fonction main. int pour integer, c’est-à-dire que la fonction main, une fois terminée, doit retourner une valeur entière. Pour information, cette valeur peut servir dans l’environnement appelant notre programme une valeur de bonne exécution ou un code erreur.
() ici vide, il s’agit de la liste des arguments fournis lors de l’appel de notre fonction.
Master IM / C++
LE HELLOWORLD LIGNE À LIGNE
{
std::cout << "Hello World" << std::endl;
}
Master IM / C++
Le corps de la fonction main.
Le corps d’une fonction se situe après l’entête de celle-ci et entre deux accolades. Elles définissent en fait le bloc d’instruction de la fonction.
Ici, il n’y a qu’une seule instruction, se terminant par un ;
std::cout peut être vu comme l’écran, il s’agit du flot de sortie standard.
<< est un opérateur opérant sur un flot de sortie à sa gauche et une donnée à lui transmettre, à sa droite.
« Hello World » est une chaine de caractère, c’est-à-dire un emplacement mémoire contigüe contenant un caractère par octet de mémoire, et se terminant conventionnellement par le caractère nul. Nous verrons cela plus en détail lorsque nous reparlerons des types de données.
std::endl demande au flux de passer à la ligne suivante.
COMPILATION ET EXÉCUTION DU
HELLOWORLD
$ g++ HelloWorld.cpp -o HelloWorld
$ ./HelloWorld.exe
Hello World Master IM / C++
Comme il s’agit d’un programme simplissime, la ligne de compilation est elle-même très simple. En la décomposant élément par élément :
g++ est le nom du compilateur c++ de GNU, celui utilisé pour ce cours.
HelloWorld.cpp est le nom du fichier dans lequel on vient d’écrire notre code.
-o HelloWorld est une option transmise au compilateur lui demandant de créer un fichier exécutable portant ce nom là. Il s’agit d’un argument optionnel. Notre programme se nommerait a.out (sous Linux, a.exe sous windows), sans cet argument.
./HelloWorld.exe dans un shell, permet d’exécuter notre programme, qui, comme prévu, affiche Hello
World et se termine.
ORGANISATION D’UN PROGRAMME EN C++
Le code source d’un programme est un ensemble de fichiers textes qui contiennent les déclarations et les définitions des différents éléments qui seront ensuite transmises au compilateur.
Un fichier se décompose généralement en 2 ou 3 parties.
Les directives préprocesseur (souvenez vous, elles commencent par #) se situent généralement en début de fichier.
Viennent ensuite les définitions et déclarations de variables ou de fonctions ou de type de données. Il ne s’agit pas d’instructions à proprement parlé, mais plutôt d’informations qui permettront au compilateur de vérifier la cohérence du code écrit ensuite. Cela peut être assez long, et, souvent le programmeur le déplace dans un fichier header suffixé en .h ou .hh qui sera inclus via une directive préprocesseur.
Enfin viennent les définitions des fonctions, le code du programme à proprement parlé, dans un fichier suffixé en .cpp pour le C++, .c pour le C.
ORGANISATION D’UN PROGRAMME EN C++
#include <iostream>
/*
Déclaration de la fonction somme
*/
double somme(double a, double b);
// point d'entrée de notre programme
int main()
{
int a, b;
std::cout << "Donnez deux entiers" << std::endl;
std::cin >> a >> b;
std::cout << a << " + " << b << "=" << somme(a, b) << std::endl;
return 0;
}
// somme retourne la somme de a et b deux réels fournis en paramétres.
double somme(double a , double b)
{
return a+b;
}
Master IM / C++
Voici l’exemple d’un programme un peu plus complexe.
On commence par déclarer une fonction somme, sans la définir, c’est-à-dire sans écrire son code.
On signifie seulement qu’il existe une telle fonction, prenant deux réels en argument et renvoyant un réel.
On peut à présent l’utiliser dans le code qui suit la déclaration. On pourrait aussi, et on doit même le faire pour plus de propreté, écrire cette partie dans un fichier header.
ORGANISATION D’UN PROGRAMME EN C++
Un tel fichier header ressemblerait à celui-ci .
On voit apparaitre 3 nouvelles directives préprocesseurs ainsi que la déclarations de la fonction somme.
On remarquera aussi que ce fichier NE CONTIENT PAS DE CODE, seulement des déclarations.
#ifndef __SOMME_HH__
#define __SOMME_HH__
/*
Déclaration de la fonction somme
*/
double somme(double a, double b);
#endif
#ifndef __SOMME_HH__
#define __SOMME_HH__
Il s’agit simplement d’une protection, évitant lors de programme plus complexe, d’inclure deux fois un fichier header. Le compilateur terminerait alors en erreur car il ne veut pas de multiples définitions (surtout lorsqu’on définira des class).
En gros, si la constante __SOMME_HH__ n’est pas définie, alors inclure le code ci après.
Dans le code en question, on commence par définir une tel constante, et on déclare notre fonction.
Enfin, on ferme la condition #ifndef par #endif
DÉCLARATIONS
En C++, avant d’utiliser une variable, une constante ou une fonction, on doit la déclarer, ainsi que son type. Ainsi, le compilateur pourra faire les vérifications nécessaires lorsque celle-ci sera utilisée dans les instructions.
Exemple de déclarations :
int i; // On déclare une variable de type entier.
float x; // On déclare une variable de type flottant (approximation
d’un nombre réel)
const int N = 5; // On déclare une constante N de type entier dont
// la valeur est 5.
Master IM / C++
VARIABLES
Une variable, comme son nom l’indique, est un espace mémoire dont le contenu peut varier au cours de l’exécution.
Master IM / C++
#include <iostream>
using namespace std;
int main()
{
int a;
a = 0;
cout << "a vaut : " << a << endl;
a = 5;
cout << "a vaut à present : " << a << endl;
}
a
00000000 00000000 00000000 00000101
… 101 102 103 104 105 106 107 108 …
a
00000000 00000000 00000000 00000000
… 101 102 103 104 105 106 107 108 …
Représentation en mémoire de a = 0
Puis on lui donne la valeur 5, son emplacement en mémoire n’a pas changé, mais le contenu si.
TYPES DE DONNÉES
Le C++ est un langage « fortement typé »
La compilation permet de détecter des erreurs de typage.
Chaque variable d’un programme a un type donné tout au long de son existence.
Un type peut représenter une valeur numérique, sur 1, 2 ou 4 octets, signé ou non, un nombre à virgule flottante dont l’encodage en mémoire est assez complexe.
TYPES DE DONNÉES NUMÉRIQUES
#include <iostream>
int main()
{
int a; // On déclare un entier a; On réserve donc 4 octets en mémoire que l’on nomme a
unsigned int b; // On déclare un entier non signé b, 4 octets sont aussi alloués char c; // On déclare un « caractère » c, un octet est réservé double reel1, reel2; //deux réels sont déclarés et la place correspondantes en mémoire est allouée
a = 0; //On attribue à a la valeur 0, jusqu’à maintenant, il n’y a pas de règle quant à sa valeur
b = -1; //On essaye de donner une valeur négative à b !
c = 'a'; //’a’ est la notation pour le caractère a.
reel1 = 1e4; //reel1 prend la valeur 10.000
reel2 = 0.0001;
std::cout << "a : " << a << " " << std::endl
<< "Interessant : "
<< "b : " << b << std::endl // HA !
<< "c ; " << c << " " << std::endl;
std::cout << reel1 << std::endl;
std::cout << reel2 << std::endl;
}
TYPES DE DONNÉES ALPHABETIQUES
Un caractère (de type char) est un élément de la table ASCII codé sur un octet. Il s’agit en fait d’un nombre entre
0 et 127.
Le caractère ‘a’ est donc la valeur 97
‘A’ se code 65
Il s’agit d’un jeu de caractère particulier.
Il y en a beaucoup d’autre, Unicode par exemple.
Master IM / C++
TYPES DE DONNÉES ALPHABÉTIQUES
Ainsi,
Donne la valeur 97 à la variable c.
Lorsqu’on affiche c. Ce n’est pas 97 qui apparait, mais ‘a’ car le compilateur sait qu’on veut afficher un caractère et non sa valeur, grâce à son type.
c = 'a';
c = 'a' + 1; Mais il s’agit bien d’un nombre, comme l’indique cet exemple, valide en C++, qui affiche le caractère suivant ‘a’ dans la table.
Soit ‘b’ !
Master IM / C++
OPÉRATEURS
Il y a des nombreux opérateurs en C++
Classiques :
Arithmétiques, relationnels, logiques
Moins classiques :
Manipulation de bits
Et des opérateurs originaux, d’affectation, d’incrémentation
Master IM / C++
OPÉRATEURS ARITHMÉTIQUES
C++ dispose d’opérateurs classiques binaires (deux opérandes)
Addition +, Soustraction -, multiplication * et division /
Il y a aussi un opérateur unaire : - pour les nombres négatifs. ( -x + y)
Master IM / C++
OPÉRATEURS ARITHMÉTIQUES
Ces opérateurs binaires ne sont à priori définis que pour des opérandes de même type parmi
Int, long int, float, double, et long double
Alors comment faire pour ajouter 1 (entier int ) et 2.5 (flottant simple précision) ? ce qui semble assez naturel ? par le jeu des conversions implicites de type. Le compilateur se chargera de convertir un opérande ou les deux dans un type rendant l’opération possible.
OPÉRATEURS ARITHMÉTIQUES
#include <iostream>
int main()
{
int a = 1;
double b = 3.14;
double c = a +b;
std::cout << sizeof(a) << ": " << a << std::endl;
std::cout << sizeof(b) << ": " << b << std::endl;
std::cout << sizeof(c) << ": " << c << std::endl;
}
Aura comme sorti :
$ ./a.exe
4: 1
8: 3.14
8: 4.14
OPÉRATEURS ARITHMÉTIQUES
OPÉRATEUR %
Reste de la division entière : n’est défini que sur les entiers en C++ (ce n’est pas le cas en Java par exemple)
Pour les entiers négatifs : le résultat dépend de l’implémentation ! ne pas utiliser !
Par ailleurs, il est à noter que la division / est différente suivant le type des opérandes :
S’ils sont entiers alors la division est entière, sinon s’il s’agit de flottants, la division sera réelle.
OPÉRATEURS ARITHMÉTIQUES
Il n’y a pas d’opérateur pour la puissance : il faudra alors faire appel aux fonctions de la bibliothèques standard du C++.
En termes de priorité, elles sont les mêmes que dans l’algèbre traditionnel. Et en cas de priorité égale, les calculs s’effectuent de gauche à droite.
On peut également se servir de parenthèses pour lever les ambiguïtés, et rendre son code plus lisible !!
OPÉRATEURS RELATIONNELS ET DE COMPARAISONS
Il s’agit des opérateurs classiques, vous les connaissez déjà.
Ils ont deux opérandes et renvoient une valeur booléenne
<, > , <=, >= , == , !=
Les deux derniers sont l’égalité et la différence.
En effet = est déjà utilisé pour l’affectation !
PETITE FACÉTIE DES OPÉRATEURS D’ÉGALITÉ
SUR LES DOUBLES.
#include <iostream>
int main()
{
double a = 3.6;
double b = 4.5;
double c = 8.1;
if(a + b == c) {
std::cout << "a+b=c" << std::endl;
}
else {
std::cout << "a+b != c" << std::endl;
}
if(a == c-b) {
std::cout << "a = c-b" << std::endl;
}
else {
std::cout << "a != c-b" << std::endl;
}
}
OPÉRATEURS LOGIQUES
En C++ il y a trois opérateurs logiques : et (noté && ) ou noté (||) et non (noté !)
Ces opérateurs travaillent sur des valeurs numériques de tout type avec la simple convention
Nulle faux
Autre que nulle vrai
COURT-CIRCUIT DANS L’ÉVALUATION DES OPÉRATEURS LOGIQUES
La seconde opérande d’un opérateur n’est évalué que lorsque sa connaissance est indispensable
Typiquement, si on sait déjà par son premier opérande qu’un ‘ou’ ou un ‘et’ seront vrai ou faux, on n’évalue pas la deuxième partie.
On peut - mais ce n’est qu’un exemple - protéger une portion de code :
if (ptr != 0 && *ptr == 8)
Dans cet exemple, on vérifie d’abord que ptr n’est pas nul avant de le déréférencer pour comparer sa valeur pointée en mémoire.
OPÉRATEURS D’AFFECTATION ÉLARGIE
C++ permet d’alléger la syntaxe de certaines expressions en donnant la possibilité d’utiliser de condenser des opérations classiques du type: variable = variable opérateur expression
Ainsi, au lieu d’écrire a = a * b
On pourra écrire a *= b;
Liste des opérateurs d’affectation élargie
+= -= *= /= %=
|= ^= &= <<= >>=
OPÉRATEUR CONDITIONNEL
Il s’agit d’un opérateur ternaire.
Il permet des affectations du type :
Si condition est vraie alors variable vaut valeur, sinon variable vaut autre valeur.
On l’écrit de la manière suivante :
x = (cond) ? a : b;
Par exemple :
int x = (y > 0) ? 2 :3;
Master IM / C++
AUTRES OPÉRATEURS
sizeof : Son usage ressemble à celui d’une fonction, il permet de connaitre la taille en mémoire de l’objet passé en paramétre.
Opérateurs de manipulation de bit :
& ET bit à bit
| OU bit à bit
^ OU Exclusif bit à bit
<< Décalage à gauche
>> Décalage à droite
~ Complément à un (bit à bit)
STRUCTURES DE CONTRÔLES
Un programme est un flux d’instructions qui est exécuté dans l’ordre. Pour casser cette linéarité et donner au programme une relative intelligence, les langage de programmation permettent d’effectuer des choix et des boucles.
On va parler de blocs d’instructions :
Il s’agit d’un ensemble d’instructions entouré d’accolades ouvrantes et
fermantes.
{
a = 5;
…
}
Master IM / C++
STRUCTURES DE CONTRÔLES
if (expression)
instruction_1
else // l’instruction else est facultative
instruction_2
expression est une expression quelconque avec la convention
Différente de 0 vrai
Egale à 0 faux
Instruction_1 et instruction_2 sont des instructions quelconques i.e. :
- Simple (terminée par un point virgule)
- bloc
- Instruction structurée
L’INSTRUCTION IF – SYNTAXE
STRUCTURES DE CONTRÔLES
Permet dans certain cas d’éviter une abondance d’instruction if imbriquées. expression est une expression quelconque comme dans le cas de if, dont la valeur va être testé contre les constantes. constante : expression constante de type entier (char est accepté car converti en int)
Instruction : suite d’instruction quelconque
Petite subtilité : Une fois un cas positif trouvé, les instructions suivantes sont exécutées. Même si elles appartiennent à un autre cas. Ce peut être pratique, mais pas toujours. Pour éviter cela, on utilisera l’instruction break qui stoppe le flot d’exécution.
L’INSTRUCTION SWITCH – SYNTAXE
switch (expression)
{ case constante_1 : [instruction_1]
case constante_2 : [instruction_2]
...
case constante_n : [instruction_n]
[default : suite_instruction]
}
S