Programmation C++ : CM séance 01
1. Description de l'UE
Présentation :
- ECUE SINA12BL - Programmation C++
- Responsable : Edouard THIEL
- 6 séances : CM 1h30, TP 3h
Objectif :
- apprentissage progressif du langage C++ ;
- donner de bonnes bases pour les matières des semestres suivants ;
- atteindre un niveau professionnel à la fin du Master.
Intervenants :
- Édouard THIEL : CM, TP groupe 1
- Laurent TICHIT : TP groupe 2
MCCC : 1
- Note finale session unique = max { ET ; 0.7 ET + 0.3 CC }
- À l'examen :
- calculette : non
- document autorisé : un pense-bête personnel sur 1 feuille A4 recto-verso
- TP : utilisation d'IAs génératives interdite.
- Présence aux CM et TP obligatoire.
2. Introduction
2.1. Historique
Bjarne Stroustrup (Bell Labs, 1979-2002)
- 1979 : "C with Classes", fin thèse. C (1972), Simula (1962). Classes, classes dérivées...
- 1983 : Nouvelle version, "C++". Fonctions virtuelles, références, surcharge opérateurs...
- 1985 : Livre The C++ Programming Language, première utilisation commerciale.
- 1989 : version 2.0. Héritage multiple, classe abstraite, templates...
- 1998 : C++98, standardisation
- 2003 : C++03, évolution mineure
- 2011 : C++11, constexpr, rvalue reference, lambdas...
- 2014 : C++14, amélioration lambdas
- 2017 : C++17, fold expr, ...
- 2020 : C++20, module, range, span, concept, ... partiellement implémenté
- 2023 : C++23, import std, ... partiellement implémenté
Normalisé par l'ISO, portant sur les 2 aspects :
- le cœur du langage (mots clé, types, instructions, etc) ;
- la librairie standard (vector, map, IO, etc).
Version utilisée pour ce cours : C++17
2.2. Utilisation
Utilisation très variée : tous les secteurs de l'industrie, applications à performances critiques :
- aviation, défense, robotique, embarqué
- calcul scientifique, imagerie médicale
- industrie minière (prospection sismique)
- contrôle industriel en temps réel
- finance haute fréquence
- téléphonie (infrastructure, ...)
- OS, compilateurs, interpréteurs, serveurs et BD, browsers
- jeux vidéos, animations, VR, modélisation 3D, CAO
- traitement audio et vidéo, MAO, studios musique
- développement d'applications (Qt, GTKmm, SFML, ...), ...
Voir par exemple https://www.stroustrup.com/applications.html
Employabilité dans l'industrie : très forte.
2.3. Ressources
Ne pas se limiter au cours !
- Nombreux livres, choisir une édition pas trop récente (ex. annas tiret archive point org).
- https://www.learncpp.com/
- https://en.cppreference.com/w/
3. Bases
3.1. Programmes
Le langage C++ est un langage compilé :
graph LR
A[Fichier source 1] -->|compilation| B[Fichier objet 1]
C[Fichier source 2] -->|compilation| D[Fichier objet 2]
B --> E((Édition\nde liens))
D --> E
E --> F[Fichier exécutable]
L'édition de lien résout les symboles et les relie aux bibliothèques.
Les fichiers sources portent en général l'extension .cpp
; ils peuvent
être accompagnés par un fichier d'entête .hpp
.
Le langage est statiquement typé : le type de tout objet doit être connu lors de la compilation.
3.2. Hello world
Fichier hello.cpp
:
1 2 3 4 5 6 |
|
- ligne 1 : inclut les définitions du module
iostream
pour les E/S ; - ligne 2 : déclare la fonction principale
main
; - ligne 4 : commentaire ;
- ligne 5 : affiche
Hello, world!
suivi d'un retour à la ligne.
Chaque programme doit posséder exactement 1 fonction main
.
Renvoie un int
au système, 0 (succès) par défaut.
Le préfixe std::
devant cout
signifie que cout
est dans l'espace de noms
std
, c'est-à-dire dans la librairie standard.
L'objet prédéfini cout
(character output) permet d'afficher
dans la sortie standard.
L'opérateur d'injection <<
(put to) recopie l'argument à droite
dans l'argument de gauche.
On peut rendre un espace de noms visible (attention aux conflits) :
1 2 3 4 5 6 |
|
3.3. Compilation
Sous Linux (Debian, Ubuntu et dérivés, ou encore WSL / Ubuntu) :
$ sudo apt install g++
$ g++ --version
g++ (Ubuntu 20.04) 9.4.0
g++ (Ubuntu 24.04) 13.2.0
Versions supportées : voir C++ Standards Support in GCC
Compilation en un fichier objet puis production d'un exécutable :
graph LR
A[hello.cpp] -->|compilation| B[hello.o]
B --> E((Édition\nde liens))
E --> F[hello]
$ g++ --std=c++17 -Wall -c hello.cpp
$ ls
hello.cpp hello.o
$ g++ -o hello hello.o
$ ls
hello.cpp hello.o hello
$ ./hello
Hello, World!
Raccourci : compilation du fichier, production directe d'un exécutable et appel :
$ g++ --std=c++17 -Wall hello.cpp -o hello && ./hello
Hello, World!
ou encore, en définissant une fonction Bash :
$ g() { g++ --std=c++17 -Wall "$1.cpp" -o "$1" ;}
$ g hello && ./$_
Hello, World!
3.4. Définitions
Le langage définit un certain nombre de mots réservés (for
, if
, const
, ...),
voir la liste.
Un littéral est une valeur fixe, par exemple un entier (123
), un réel (3.14
),
un caractère ('a'
), une chaîne de caractères ("abc"
), un booléen (true
).
Un identificateur est un nom composé de minuscules (a-z
), majuscules (A-Z
),
chiffres (0-9
), ou du caractère souligné _
,
ne commençant pas par un chiffre ;
il est sensible à la casse.
Il ne peut pas contenir de caractère accentué.
Exemples : x
, y0
, _z
, point_color
, Rectangle
, myData
.
Les variables, types et fonctions sont des identificateurs.
Tout identificateur doit être déclaré (c'est-à-dire défini) avant d'être utilisé (sauf les noms prédéfinis par le langage ou le compilateur).
Chaque instruction est terminée par un ;
.
Les instructions peuvent être groupées par des accolades {}
.
Les indentations et espaces dans le code sont ignorés.
3.5. Types de base
Chaque nom et chaque expression a un type, qui permet de déterminer les opérations supportées.
Il existe de nombreux types prédéfinis. Les types de base (ou types fondamentaux) sont :
-
les types entiers
bool
: booléen, valeurstrue
etfalse
char
: caractère, par exemple'a'
,'0'
int
: entier, par exemple123
,-5
std::size_t
: entier non signé résultat de l'opérateursizeof
-
les types flottants
double
: réel en double précision, par ex.0.
,3.14
,-1.2e-34
float
: réel simple précision
-
le type vide :
void
: pas de valeur
Pour les entiers :
-
Les littéraux sont en base 10 ; on peut préfixer par
0x
(hexadécimal),0
(octal),0b
(binaire, C++14). Exemple :14 = 0xe = 016 = 0b1110
. -
La taille (en bits) varie en fonction des machines (32/64 bits, ...), des options de compilation, et des modificateurs
short
,long
etunsigned
. -
Les modificateurs peuvent être combinés et répétés, par exemple
signed short int
, ouunsigned long long int
; on peut omettreint
. -
un littéral peut être suffixé, par exemple
123ll
est unlong long
,45uz
est ununsigned std::size_t
, voir liste. -
Le standard C++ garantit que
1 == sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long).
Pour les flottants :
- les littéraux contiennent un
.
ou une
; - les chiffres peuvent être groupés par des
'
pour faciliter la lecture (exemple2,718'281'828
).
On peut déclarer un alias d'un type :
typedef le_type_existant alias_du_type;
typedef unsigned long long My_ulong;
Le C++ donne aussi la possibilité de créer des user-defined literals
avec des suffixes, par exemple 12_km
.
3.6. Variables
Une variable est déclarée sous la forme d'un identificateur, précédé d'un type :
type_variable nom_variable;
Exemples de déclarations de variables :
int i;
double x, y;
bool is_sorted;
Toute variable doit être initialisée avant utilisation :
i = 5;
x = 3.1; y = 0.;
is_sorted = false;
Déclaration avec initialisation : évite des oublis
int i = 5;
double x = 3.1, y = 0.;
bool is_sorted = false;
Déclaration automatique avec auto
: pratique lorsque le type peut être
déduit par le compilateur :
auto i = 5;
auto x = 3.1, y = 0.;
auto is_sorted = false;
// mais pas : auto i = 5, x = 3.1, y = 0., is_sorted = false;
Syntaxe avec accolades
int i {5}; // ou encore : int i = {5};
i = {7};
Le =
dans l'initialisation est l'opérateur provenant du C ;
la forme {}
est plus générale,
et permet d'éviter des conversions où il y a une perte d'information :
int i = 3.14; // conversion implicite : i devient 3
int i {3.14}; // erreur : conversion d'un réel en entier
double x = 3; // conversion implicite : x devient 3.0
double x {3}; // conversion sans perte d'information
Exemple avec un type élaboré :
#include <complex>
std::complex<double> z {1., 2.};
z = {3, 4.12};
Durée de vie
On peut déclarer une variable n'importe où dans un bloc entre accolades { }
;
la variable existe depuis sa déclaration jusqu'à la fin du bloc, où elle
est "détruite".
{ // début bloc
....
type_variable v; // création de v
... // utilisation de v
} // fin bloc --> destruction de v
3.7. Entrées-sorties
Les objets suivants sont prédéfinis dans <iostream>
:
std::cin
(character input) : lit dans l'entrée standard ;std::cout
(character output) : affiche dans la sortie standard ;std::cerr
(character error) : affiche dans la sortie d'erreur ;std::endl
(end line) : envoie"\n"
puis force l'affichage (flush).
Les objets cin
, cout
et cerr
opèrent sur un flux (stream) à l'aide des
opérateurs suivants :
<<
(put in) : injection, pour les affichages ;>>
(get from) : extraction, pour les lectures.
L'instruction
std::cout << valeur;
évalue la valeur, puis l'injecte dans l'objet cout
, qui l'affiche en fonction
du type de la valeur, puis renvoie l'objet cout
lui-même.
Quel est l'effet de cette ligne ?
std::cout << valeur1 << valeur2;
En fait, l'opérateur d'injection est associatif à gauche, donc la ligne est équivalente à
(std::cout << valeur1) << valeur2;
or (std::cout << valeur1)
renvoie l'objet cout
, donc la ligne est équivalente à
std::cout << valeur1;
std::cout << valeur2;
De même pour std::cerr <<
et std::cin >>
.
L'extraction fonctionne comme ceci : une lecture est faite dans l'entrée
standard, puis convertie selon type_variable
et la valeur est mémorisée dans
la variable v
fournie :
type_variable v;
std::cin >> v;
Exemple d'une saisie dans le terminal :
1 2 3 4 5 6 7 8 9 |
|
Affiche :
Entrez deux entier :
5 7
Vous avez entré 5 et 7
L'affichage peut être paramétré avec iomanip :
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Affiche :
z = (1.0,2.0)
z = (3.0,4.1)
3.8. Fonctions
Une fonction
- désigne un bloc de code qui peut être exécuté ;
- possède un nom, un type de retour, des paramètres typés, un corps ;
- doit obligatoirement être déclarée avant de pouvoir être utilisée.
Syntaxe de la déclaration :
type_retour nom_fonction (type_paramètre paramètre, ....)
{
// corps de la fonction
....
return valeur_retour; // sortie immédiate de la fonction
....
}
Syntaxe d'un appel de la fonction (exécution de son corps) :
type_retour variable = nom_fonction (paramètre, ....);
Exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Ligne 8 : un type de retour void
indique que la fonction ne renvoie pas de
valeur.
Lignes 16,17 : appel des fonctions.
Déclaration forward (en avance)
on peut déclarer partiellement une fonction avant de l'écrire entièrement. Syntaxe :
type_retour nom_fonction (type_paramètre paramètre, ....); // declaration forward
type_retour nom_fonction (type_paramètre, ....); // variante
type_retour nom_fonction (type_paramètre paramètre, ....) // implémentation
{
// corps de la fonction
}
Exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Utilité :
- rend l'ordre de l'implémentation indépendant de l'ordre d'appel ;
- mécanisme utilisé dans les headers
.hpp
(ce sera vu plus tard).
Signature et surcharge d'une fonction
La signature est constituée du nom de la fonction et des types des paramètres ; le type de retour et les noms des paramètres sont omis :
nom_fonction (type_paramètre1, ....)
Le C++ permet de déclarer une fonction avec des signatures différentes : on appelle cela la surcharge de la fonction (overloading) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Affiche :
2 * 3 = 6
2 * 3 * 4 = 24
2.0 * 3.0 = 6.0
Remarque : afficher_produit (2., 3);
provoquerait une erreur de compilation :
error: call of overloaded ‘afficher_produit(double, int)’ is ambiguous
Paramètres optionnels
On peut rendre des paramètres optionnels en leur donnant une valeur par défaut. Ces paramètres doivent être placés après les paramètres obligatoires. On peut alors les omettre lors de l'appel :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Affiche :
3, 4, 0, 1
3, 4, 5, 1
3, 4, 5, 6
3.9. Structures de contrôle
Il s'agit des branchements if
else
, switch
case
, des boucles
while
et for
, et des exceptions.
condition
est une expression booléenne ;instruction
désigne une instruction simple, composée ou d'un bloc d'instructions entourées d'accolades{ }
.
Branchements if-else
Syntaxe :
if (condition) instruction;
if (condition) instruction1; else instruction2;
Style préconisé par Stroustrup :
if (condition)
instruction;
if (condition) {
instructions;
}
if (condition) {
instructions1;
}
else {
instructions2;
}
if (condition1) {
instructions1;
}
else if (condition2) {
instructions2;
}
else {
instructions3;
}
Branchement switch-case
valeur
est une constante connue à la compilation.
switch (expression) {
case valeur1:
instructionsA;
break;
case valeur2:
instructionsB;
break;
case valeur3:
case valeur4:
instructionsC;
break;
....
default :
instructionsN;
}
Boucle while
La boucle la plus générale :
while (condition) {
instructions;
}
Variante, qui effectue au moins une itération :
do {
instructions;
} while (condition);
Boucle for
for (instruction_init; condition; instruction_next) {
instructions;
}
équivalente à
instruction_init;
while (condition) {
instructions;
instruction_next;
}
Dans instruction_init
on peut aussi déclarer des variables, qui n'existeront
que pendant la durée de la boucle. Exemple :
1 2 3 4 5 6 7 8 |
|
Affiche :
0 1 2 3 4
Boucle infinie :
for (;;) {
instructions;
}
Il existe aussi une forme de boucle for
qui itère dans une collection (range-for).
Syntaxe :
for (variable : collection) {
instructions;
}
Exemple :
1 2 3 4 5 6 7 8 9 |
|
Affiche :
5 7 11
Contrôles
break
sort de la boucle la plus imbriquée ou d'unswitch
;continue
passe immédiatement à l'itération suivante (la plus imbriquée) ;return valeur
oureturn
sort immédiatement d'une fonction, renvoievaleur
ou rien (fonctionsvoid
) ;std::exit(n)
défini dans<cstdlib>
termine le programme avec le code de sortien
(0 pour succès).
Exceptions
Le flot normal de l'exécution peut être interrompu par une exception :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Affiche :
avant try-catch
avant throw
catch int 123
après try-catch
3.10. Opérateurs
Les opérateurs arithmétiques agissent sur les types fondamentaux :
x+y
: addition,+x
: plus unairex-y
: soustraction,-x
: moins unairex*y
: multiplication,x/y
: division,x%y
: reste de la division
Opérateurs d'assignement :
x += y
(x = x+y
),++x
oux++
(x = x+1
) : incrémentx -= y
(x = x-y
),--x
oux--
(x = x-1
) : décrémentx *= y
(x = x*y
),x /= y
(x = x/y
),x %= y
(x = x%y
)
Opérateurs de comparaison, résultat booléen :
x == y
: est égal,x != y
: est différentx < y
: est strictement inférieur,x <= y
: est inférieur ou égalx > y
: est strictement supérieur,x >= y
: est supérieur ou égal
Opérateurs logiques (sur booléens) :
x && y
: et logique,x || y
: ou logique!x
: négation
Opérateurs de bits (sur entiers non signés) : 2
x & y
: et binaire,x | y
: ou binaire,x ^ y
: ou exclusif~x
: complémentx << y
: décalage à gauche,x >> y
: décalage à droite
Il est possible de redéfinir des opérateurs par surcharge pour agir sur des objets ; ce sera vu plus tard.
-
MCCC = Modalités de contrôle des connaissances et des compétences ; ET = Examen Terminal ; CC = Contrôle Continu. ↩
-
Voir Bit Twiddling Hacks by Sean Eron Anderson ↩