Logo HE-ARC Logo HES-SO

1242.2 Langage C++ - 2025-2026

Chapitre 1

De C à C++

1. Introduction

2. Espaces de noms

3. Entrées/sorties standards

4. Passage de paramètres

5. Valeurs par défaut des paramètres

6. Surcharge des fonctions

7. Allocation dynamique

8. La classe std::string

9. Divers : auto, range-for. etc.

10. Bibliothèque standard

1.1 Historique du langage

1979 - C avec des classes : premières extensions au langage C

1983 - le nom C++ est créé pour "C incrémenté"

1998 - Le langage C++ est normalisé par l'ISO

2011 - C++11, C++ moderne : nouvelle norme du langage

2014 - C++14 : mise à jour mineure

2017 - C++17 : mise à jour majeure (amélioration des templates, etc)

2020 - C++20 : mise à jour majeure (module, coroutines, etc.)

2023 - C++23 : mise à jour mineure, version courante

2026 - version en cours de développement



📚 Historique de C++ (Wikipedia)

1.1 Introduction

⚠️ Ce cours ne revient pas sur le C !


Le C++ est une extension du langage C (un compilateur C++ peut compiler du code C)

Le C++ est libre de droit

Au-delà de ~50'000 lignes de code, le C ne suffit plus

1.1 C++ est-il un langage objet ?

Oui

C++ contient la notion de classe, d'instances, d'héritage, etc.


Non

C++ est surtout un langage multiparadigme

C++ est entièrement compatible avec C qui n'est pas un langage objet

On peut écrire un programme « C++ » sans jamais utiliser la notion d'objet

1.1 Lien avec C

Pour programmer en C++, il « faut » connaître le langage C (notions & syntaxe)

En C++, il est possible :

  • de ne pas du tout utiliser les objets (on fait du C)
  • de toujours utiliser les objets (on fait du C++ pur)
  • de mélanger des objets avec de la programmation non-objet

Le C++ peut ne pas être "propre" : il hérite du C, de notions inutiles en POO


⛔ Éviter de mélanger du C avec du C++

✅ Préférer le C++ pur

Pourquoi le C++

1.1 Un premier exemple



            #include <iostream>
            
            int main()
            {
              std::cout << "Hello world++!" << std::endl;

              return 0;
            }

Fonctionnalités avancées accessibles avec la bibliothèque standard C++

std Espace de noms (namespace)

:: Opérateur de portée

cout Flux de sortie

<< Opérateur d'insertion

1.1 Déclaration de variables

Les variables peuvent être déclarées n'importe où (comme en C99)



            #include <print>

            int main()
            {
                for (int i = 0; i < 3; ++i)
                {
                    int x = i * 2;
                    std::println("{}", x);
                }

                return 0;
            }
          

✅ Initialiser les variables à la déclaration

1.1 Booléens

Une variable de type bool a 2 valeurs : true et false


Conversions avec int

true : 1

false : 0

Entier non-nul : true

0 : false

Conversions avec les pointeurs

pointeur non-nul : true

pointeur nul : false


✅ Ne pas utiliser ces conversions implicites (lisibilité)

if (x != 0) plutôt que if (x)

if (ptr != nullptr) plutôt que if (ptr)

1.2 Espace de noms

Grands projets ⇒ risque de collision de noms (variables et fonctions)


✅ Confiner les noms d'un module dans un espace de noms (namespace)

namespace XX{...} : définit un nouvel espace de noms

namespace XX::YY{...} : nouvel espace de noms imbriqués (C++17)

using namespace XX : ouvre l'espace de noms XX

:: : opérateur de résolution de portée

1.2 Espace de noms - Exemple


            
              namespace language::english
              {
                std::string colors[] = {"White", "Red", "Black"};
                void colorName(int index)
                {
                  std::cout << "The color is: " << colors[index] << std::endl;
                }
              }

              namespace language
              {
                namespace french
                {
                  std::string colors[] = {"Blanc", "Rouge", "Noir"};
                  void colorName(int index)
                  {
                    std::cout << "La couleur est : " << colors[index] << std::endl;
                  }
                }
              }
          
          

1.2 Espace de noms - Exemple

            

              using namespace language;

              int main()
              {
                // GCC: error: 'colorName' was not declared in this scope
                // colorName(1);

                english::colorName(1);

                {
                  using namespace french;
                  colorName(1); // -> "Rouge"
                }

                using namespace english;
                colorName(1);         // -> "Red"
                french::colorName(1); // -> "Rouge"

                using namespace french;

                // GCC: error: call of overloaded 'colorName(int)' is ambiguous
                // colorName(1);

                return 0;
              }
          
          

1.3 Entrées / Sorties

Entrées / Sorties avec les variables prédéfinies

std::cin est une source de flux de données, en général le clavier

std::cout est une destination de flux de données, en général l'écran


On utilise les opérateurs

>> extraction d'un flux source

<< insertion dans un flux destination


Exemple


            #include <iostream>

            ...
            
            int n;
            std::cin >> n; // Read from keyboard
            std::cout << "val " << n; // Print to screen
          

1.3 Entrées / Sorties

Inclure iostream

Accessibles dans l'espace de noms std


Exemples


                #include <iostream>
    


                int main()
                {
    
                  ...
               
                  std::cout << "val " << n; // Print to screen
                }
              

                #include <iostream>
    
                using namespace std;
    
                int main()
                {
    
                  ...
               
                  cout << "val " << n; // Print to screen
                }
              

📚 Input/output library (cppreference)

1.3 Manipulateurs d'E/S standard

Les manipulateurs permettent de modifier un flux

std::endl : fin de ligne

std::dec, std::oct, std::hex : changement de base

std::[no]uppercase : changement de casse

std::[no]showpos : force l'affichage du signe +

std::[no]boolalpha : affichage booléen (persistant)

Exemples


            #include <iostream>

            ...

            int j = 10;
            std::cout << std::hex << std::nouppercase << j << std::endl;
            std::cout << std::hex << std::uppercase << j << std::endl;
            std::cout << std::dec << std::showpos << j << std::endl;
          

1.3 Manipulateurs d'E/S standard

⚠️ Attention : les manipulateurs qui prennent des arguments appartiennent à la bibliothèque iomanip


setprecision(int) : nombre de chiffres significatifs

fixed : représentation à virgule fixe

scientific : représentation scientifique

defaultfloat : représentation par défaut

setw(int) : nombre de caractères utilisés

setfill(char) : caractère de remplissage



📚 Input/output manipulators (cppreference)

1.3 Manipulateurs d'E/S standard

Contrôler l'état du flux d'entrée et agir

cin.fail() : problème dans le flux d'entrée, saisie incorrecte

istream& ignore(streamsize n=1, int delim = EOF) : vide le buffer d'entrée


⚠️ Attention : il faut utiliser clear() avant d'utiliser ignore() pour remettre le flux d'entrée dans un état correct

1.3 Manipulateurs d'E/S standard

Exemple



            int N = 0;
            std::cout << "Enter a number between 1 and 6: ";
            while (!(std::cin >> N) || N < 1 || N > 6)
            {
              if (std::cin.fail())
              {
                std::cout << "Wrong input, try again: ";
                std::cin.clear();
                std::cin.ignore(256, '\n');
              }
              else
              {
                std::cout << "The number is out of range: ";
              }
            }

Affichage


  
              Entrez un chiffre entre 1 et 6 : abcdef 
              Saisie incorrecte, recommencez : -3 
              Le chiffre n'est pas entre 1 et 6: 17 
              Le chiffre n'est pas entre 1 et 6: 5
              

1.3 Entrées / Sorties (C++23)

Inclure print


              
              #include <iostream>
              #include <string>
              #include <format>
              #include <print>
              
              // C++20: std::format and {:.2} instead of std::setprecision
              double dbl = 3.1415926535897932384626433832795;
              std::cout << std::format("{:.2f}", dbl) << std::endl;
              std::cout << std::format("{:.8f}", dbl) << std::endl;
              std::cout << std::format("{:.4e}", dbl) << std::endl;
              std::cout << std::format("{:.2f}", dbl) << std::endl;
              
              // C++23: print and println instead of std::cout and std::endl
              std::string firstname = "Donald";
              std::string lastname = "Knuth";
              int answerToEverything = 42;
              
              std::println("Hello, {} {}!", firstname, lastname);
              std::print("The answer is = {}\n", answerToEverything);

📚 std::print (cppreference)

1.4 Passage par référence

Une référence à une variable de type T est de type T&


Une référence est constante : elle ne peut être modifiée qu'à sa déclaration



            int i;
            int &r = i; // r is a reference to i
            r = 99; // affects i to 99

          

Alternative aux pointeurs

Surtout utilisées pour les passages de paramètres

⚠️ Attention : on ne dispose pas de l'arithmétique des pointeurs !

⚠️ Attention : la signification des opérateurs * et & dépend du contexte !


📚 La déclaration de références en C++

1.4 Modes de passage des paramètres

Par valeur


            
            void fn(int param_formel){...}
            fn(param_effectif);
          
          

Par adresse



            void fn(int *param_formel){...}
            fn(&param_effectif);
          
          

Par référence



            void fn(int &param_formel){...}
            fn(param_effectif);
          
          

Par référence sur une constante



            void fn(const int &param_formel){...}
            fn(param_effectif);
          

1.4 Passage par référence II

Passer des paramètres modifiables aux fonctions, sans utiliser les pointeurs


Déclaration et définition



          void swap(int &a, int &b)
          {
            int t = a;
            a = b;
            b = t;
          }
        

Appel



          int x=2, y=3;
          swap(x, y);
        

1.4 Références sur des données constantes

L'argument pasTouche est nécessaire mais ne doit pas être modifié


Passage par valeur



            double CalculerTout(monType pasTouche)
            {
              ...
            }
          
          

⛔ création d'une copie de l'argument


Passage par référence constante



            double CalculerTout(const monType &maRef)
            {
              ...
            }

          

✅ L'argument n'est pas copié, et ne peut pas être modifié

1.4 Références

⚠️ Attention à l'opérateur &

3 significations différentes selon le contexte




          int &r = i; // ?

          p = &i; // ?
          
          z = a&b; // ?
        

🌶️ 1.4 Fonction renvoyant une référence

Exemple



            int& getRefOnCount()
            {
              static int count = 0;
              return count;
            }

            int main()
            {
              std::println("Count: {}", getRefOnCount()++);
              std::println("Count: {}", ++getRefOnCount());
            }
          

Affichage



          Count: 0
          Count: 2
        

🌶️ 1.4 Fonction renvoyant une référence

Manipulation au travers d'une fonction-sécurité


    
              #include <iostream>
              #include <string>
              #include <stdexcept>
    
              const int N=2;
              std::string tabNoms[N] = {"Bob", "John"};
              int tabAges[N] = {20, 30};
    
              int& age(const std::string &nom)
              {
                for (int i = 0; i < N; ++i)
                {
                  if (nom == tabNoms[i])
                  {
                    return tabAges[i];
                  }
                }
                
                // If we are here, the name was not found
                // See chapter on exceptions for more details
                throw std::out_of_range("nom not found");
              } 

            

   
              ...
    
              int main()
              {
                age("Bob") = 50;
                age("John")++;

                return 0;
              }
            

1.5 Valeur par défaut des paramètres

Les valeurs sont précisées à la déclaration et non à la définition

Les paramètres peuvent être omis à l'appel


          
          void trier(void *tab, int nbr, int size=100, bool up=true);
        

Appels possibles


          
          trier(tab, nb, 4); // => size=4, up=true
          trier(tab,nb); // => size=100, up=true
          

⚠️ Attention : les paramètres par défaut doivent être à la fin de la liste


📚 Paramètres par défaut (cppreference)

1.6 Surcharge des fonctions

Signature d'une fonction : liste des types des paramètres d'une fonction

Surcharge d'une fonction : écriture de plusieurs fonctions de même nom, mais de signature différente


Exemple


                
                int add(int a, int b)
                {
                  return a + b;
                }
                int add(int a, int b, int c)
                {
                  return a + b + c;
                }
                double add(double a, double b)
                {
                  return a + b;
                }
                double add(double a, double b, double c)
                {
                  return a + b + c;
                }

1.7 Allocation dynamique de mémoire

new : est suivi d'un type et réserve de la mémoire pour ce type et renvoie un pointeur (ou une exception)

new T ≡ malloc(sizeof(T))


delete : est suivi d'une variable contenant une adresse et il libère cette mémoire


Exemple



          double *ptr = nullptr;
          ptr = new double; // Réservation de mémoire
          delete ptr;       // Libération de mémoire
          ptr = nullptr;    // Sécurité
        

1.7 Allocation dynamique de tableaux

Exemple


          int *pTableau = nullptr;
          pTableau = new int[10]; 
          delete [] pTableau;
          pTableau = nullptr;
        

new int[10] alloue de la place pour 10 entiers (40 bytes)

⚠️ Attention : ne pas oublier []. Sinon, seul le 1er élément est libéré ⇒ fuite mémoire

GCC warning
'void operator delete(void*, std::size_t)' called on pointer returned from a mismatched allocation function [-Wmismatched-new-delete]

1.7 Allocation dynamique : gestion d'erreurs

En cas d'échec d'allocation, new lève une exception std::bad_alloc


Exemple


            // See chapter on exceptions for more details
            try
            {
              // throws std::bad_alloc if no more memory is available
              x = new int[1024 * 1024];
            }
            // See chapter on exceptions for more details
            catch (const std::bad_alloc &e)
            {
              std::println("Memory allocation failed: {}", e.what());
            }
          

📚 new (cppreference)

1.7 Allocation dynamique : gestion d'erreurs

⛔ Éviter d'utiliser cette version quand on utilise les exceptions pour gérer les erreurs

new(std::nothrow) se contente de renvoyer nullptr en cas d'échec.


Exemple


                  int *ptr = new (std::nothrow) int[1024];
                  if(ptr == nullptr)
                  {
                    ...
                  }
                

📚 new (cppreference)

1.8 La classe std::string

Type (classe) pour les chaînes en C++ : std::string

Plus simple et plus sûr que char*

Gestion automatique/dynamique de la taille + opérateurs et fonctions dédiées

std::string est déclaré dans <string> et visible dans std


                #include <string>
                
                int main()
                {
                  std::string x = "Hello";
                  // Or
                  // auto x = std::string("Hello");
                  
                  // C++14: string literals
                  // Need to add "using namespace std::string_literals;"
                  using namespace std::string_literals;
                  auto s = "Hello"s;
                }
              

1.8 La classe std::string

Opérateurs

Affectation : =

Concaténation : +, +=

Comparaisons : ==, <, >, <=, >=

Méthodes

int size() : renvoie le nombre de caractères

int length() : renvoie le nombre de caractères

int capacity() : taille possible sans réallocation

bool empty() : vrai si la chaîne est vide, faux sinon

int find(string s, int pos) : position de la chaine s dans la chaine courante à partir de pos

string substr(int start, int length) : sous-chaine de longueur length à partir de start


📚 std::string (cppreference)

1.8 La classe std::string

Exemple


                #include <iostream>
                #include <string>
                
                int main()
                {
                  std::string x = "toto";
                  std::string y = x;
                  std::string z = x + "_" + y;

                  std::println("{} {} {}", x, y, z);
                  std::println("cap: {}, size: {}", z.capacity(), z.size());

                  return 0;
                }
              

Affichage


                toto toto toto_toto
                cap: 15
                size: 9
              

1.8 Conversion de std::string (sans flux)

Conversion de const char* vers std::string


                const char *ptr2char = "Hello";
                std::string mystring(ptr2char);
              

⚠️ Attention : on passe d'un tableau de caractères (pointeur) à un objet (structure)


Conversion de std::string vers const char*


                std::string mystring = "Hello";
                const char *ptr2char2 = nullptr;
                ptr2char2 = mystring.c_str();
              

⚠️ Attention : on passe d'un object (structure) à un tableau de caractères (pointeur)

1.8 Le flux std::string

Les flux permettent de convertir des données en chaînes de caractères

La source ou la destination d'un flux peut être une chaîne de caractères

⇒ Les 2 sont constitués d'octets

Intérêts : conversions, transferts en mémoire (buffers)


#include <sstream> ⇒ 3 classes


istringstream : entrée

ostringstream : sortie

stringstream : les deux


📚 std::stringstream (cppreference)

1.8 Conversion de std::string (avec flux)

Conversion de int vers std::string


                #include <sstream>
                int main()	
                {		
                  std::ostringstream toto;
                  toto << 10;
                  std::string texte = toto.str();
                }
              

Conversion de std::string vers int


                #include <sstream>
                int main()	
                {		
                  std::istringstream titi("10");
                  int nombre;
                  titi >> nombre;
                }
              

1.9 Les fonctions inline

Fonctions dont le code est copié à chaque appel

Utilisées pour des fonctions courtes et fréquemment appelées

Macro en C


                #define MAX(x,y) ((x)>(y)?(x):(y))
              

inline en C++


                inline int max(int x, int y) { return x>y ? x : y; }
              

Les expressions x=MAX(2,b+1); et x=max(2,b+1); sont remplacées par le compilateur par x=((2)>(b+1)?(2):(b+1));

1.9 Les struct en C++

En C++, une structure peut aussi contenir des fonctions (méthodes)

Exemple


                struct Vecteur
                {
                  double x, y;
                  double longueur()
                  {
                    return (sqrt(x*x + y*y));
                  }
                };
                Vecteur v;
                v.x=3.0; v.y=4.0;
                v.longueur();
              

📌 Pas besoin de mentionner struct

1.9 divers

Utiliser nullptr, et non 0 ou NULL

Terminer les fichiers source par une ligne vide pour des raisons de compatibilité avec certains anciens outils qui peuvent ignorer une ligne se terminant par EOF

Utiliser auto pour l'inférence de type

Utiliser decltype pour obtenir le type d'une expression

⇒ Très utile pour les itérateurs et les templates

1.9 Range-based for loop

            
                        int values[] = {10, 20, 26};

                        for (const auto& value : values) 
                        {
                          std::println("{}", value);
                        }
                      

Affichage


                        10
                        20
                        26
                      

📚 Range-based for loop (cppreference)

1.10 Bibliothèque standard

⚠️ Plus de détails au chapitre 9 : bibliothèque standard

Structures de données classiques (avec les algorithmes associés)

Intégrée à la procédure de standardisation de C++

Basée sur les modèles de classes (conteneurs valables pour plusieurs classes/types différents)


Contient

  • Conteneurs : implémentation de structures de données classiques (Chapitre 9)
  • Itérateurs : offrent des méthodes unifiées d'accès aux conteneurs (Chapitre 9)
  • Algorithmes : offrent des algorithmes pour les conteneurs (Chapitre 9)
  • Foncteurs : objets fonctions (non traités dans ce cours)


📚 Bibliothèque standard (cppreference)

1.10 Conteneurs et itérateurs

Conteneurs

Objets permettant de stocker d'autres objets

Permettent de gérer les objets contenus : ajout, suppression, insertion


Itérateurs

Permettent de parcourir une collection d'objets sans avoir à se préoccuper de l'implémentation. Ceci permet d'avoir une interface de manipulation commune

Utilisation possible d'algorithmes comme tri, recherche, remplacement

1.10 Les conteneurs

2 types de conteneurs : séquentiels et associatifs


Les conteneurs séquentiels

Conteneurs dont les éléments sont ordonnés

On peut parcourir le conteneur suivant cet ordre et insérer ou supprimer un élément en un endroit explicitement choisi

Exemples : vecteurs, listes, piles, files, ...


Les conteneurs associatifs

Conteneurs dont les éléments sont identifiés par une clé et ordonnés suivant celle-ci

Pour insérer un élément, il n'est en théorie plus utile de préciser un emplacement

Exemples : dictionnaires, ensembles

1.10 Les classes conteneurs

1.10 Les classes conteneurs

#include <...>


Conteneurs séquentiels

Tableau dynamique contigu : vector

Tableau statique contigu : array

File à double entrée : deque

Liste doublement chainée : list

Liste simplement chainée : forward_list

Conteneurs associatifs

Dictionnaires (clé ⟷ valeur) : map, multi_map

Ensembles : set, multi_set


Adaptateurs de conteneurs

Files : queue, priority_queue

Piles : stack

1.10 Méthodes courantes des conteneurs

clear() : vide le conteneur

size() : retourne le nombre d'éléments

empty() : teste si le conteneur est vide

push_back(...) : ajoute un élément à la fin

push_front(...) : ajoute un élément au début (std::list, std::deque,...)

front() : renvoie le 1er élément

pop_front() : supprime le premier élément

insert() : insère un élément

erase() : supprime un élément

sort() : trie le contenu d'un conteneur

find() : recherche dans le conteneur

1.10 Conteneur séquentiel : std::vector


                        #include <vector>
        
                        int main()
                        {
                          // {10, 11}
                          std::vector<int> myVector1{10,11};

                          // {10.0, 11.0}
                          std::vector<double> myVector2;
                          myVector2.push_back(10.0);
                          myVector2.push_back(11.0);
                          
                          // {5, 5}
                          std::vector<int> myVector3(2,5);
                          // {10, 11}
                          myVector3[0]=10;
                          myVector3[1]=11;
                          ...                  
                        }
                      

1.10 Conteneur séquentiel : std::vector


            for (int item : myVector1)
            {
              std::print("{} ", item);
            }
            
            for (const int& item : myVector1)
            {
              std::print("{} ", item);
            }
            
            for (const auto& item : myVector1)
            {
              std::print("{} ", item);
            }
            
            for (int i = 0; i < (int)myVector1.size(); i++)
            {
              std::print("{} ({}) ", myVector1[i], myVector1.at(i));
            }

1.10 Conteneur séquentiel : std::vector


            #include <iostream>
            #include <vector>
            #include <algorithm> // std::sort
            
            int main()
            {
              std::vector<int> v{5, -4, 9, 5, 3, 0};

              // Ascending order
              std::sort(v.begin(), v.end());
              
              // Descending order
              std::sort(v.begin(), v.end(), std::greater<int>());

              return 0;
            }

1.10 Conteneur séquentiel : std::vector


            #include <iostream>
            #include <vector>
            #include <string>

            int main()
            {           
              std::vector<std::string> v;
              v.push_back("Introduction");
              v.push_back("une phrase");
              v.push_back("Conclusion");
              for(int i = 0; i < v.size(); ++i)
              {
                std::println("{}", v[i]);
              }              
              return 0;
            }

1.10 Conteneur séquentiel : std::array


              #include <array>

              int main()
              {
                std::array<int, 2> myArray1{10,11}; // 10 11
                std::array<int, 2> myArray2{0}; // 0 0

                std::array<int, 2> myArray3;
                // myArray3.push_back(10); => not possible
              
                std::array<int, 2> myArray4;
                myArray4[0] = 10;
                myArray4[1] = 11; // 10 11

                return 0;
              }

1.10 Imbrication de conteneurs

Il est possible d'imbriquer les conteneurs

On peut par exemple, créer un vecteur de listes de chaînes de caractères

std::vector<std::list<std::string>> maChanson;


La définition d'un type intermédiaire peut augmenter la lisibilité

typedef std::list<std::string> Couplet;

std::vector<Couplet> maChanson;

1.10 Imbrication de conteneurs

Des objets std::vector imbriqués simulent des tableaux multidimensionnels

On peut initialiser une matrice identité M (5x5) avec le code suivant :


            using Line = std::vector<double>;
            using Matrix = std::vector<Line>;

            Matrix m(5); 	// 5 lines
            for(int i = 0; i < 5; ++i)
            {
              m[i].resize(5);   // rows size is 5
              for(int j = 0; j < 5; ++j)
              {
                if (i==j)
                {
                  m[i][j]=1;
                }
                else
                {
                  m[i][j]=0; 
                }
              }
            }

Mots-clés en C++