1242.2 Langage C++ - 2025-2026
1. Introduction
2. Définition d'une classe
3. Implémentation d'une méthode
4. Instancier des objets
5. Les constructeurs
6. Le destructeur
7. Membres constants
8.
9. Diagrammes UML
10.
Procédurale / impérative
Séparation des données et des fonctions
Une fonction agit sur des données
Orientée objet (POO)
Regroupement des fonctions et données dans un objet
Envoi de "messages" entre objets
Approche naturelle
Code réutilisable
Modulaire
Plus facile à maintenir / à faire évoluer
Meilleure gestion pour les gros projets
int balance = 0;
void deposit(int x)
{
balance += x;
}
int main(void)
{
deposit(15);
return 0;
}
class Account
{
int balance{0};
void deposit(int x)
{
balance += x;
}
};
int main()
{
Account myAccount;
Account otherAccount;
myAccount.deposit(15);
return 0;
}
Une classe est un
Un objet est une
Une classe est la description abstraite d'un objet. Elle regroupe des
Appelés aussi des variables membres (de la classe)
Décrivent l'état de l'objet
Exemples : vitesse, poids, nombre de passagers, etc.
Appelées aussi des fonctions membres (de la classe)
Décrivent les actions qu'un objet peut effectuer
Exemples : démarrer, accélérer, freiner, etc.
Définition de la classe
#include <print>
// Class name: Point
class Point
{
public:
// Methods
void init(int, int);
void translate(int, int);
void print()
{
std::println("({},{})", x, y);
}
// Attributes
int x{0};
int y{0};
};
Définition
...
void Point::init(int _x, int _y)
{
x=_x;
y=_y;
}
void Point::translate(int delta_x, int delta_y)
{
x += delta_x;
y += delta_y;
}
...
Utilisation
int main()
{
// Point is a class
// p1 is an object of the class Point
Point p1;
p1.init(-1,2);
p1.print();
p1.translate(10,10);
p1.print();
return 0;
}
Un objet possède un
C'est l'ensemble des valeurs qui décrit l'état de l'objet à un instant donné. Cet état n'est pas directement accessible : l'utilisateur doit y accéder au travers de son interface.
Un objet possède une
C'est la façon dont l'objet est réalisé. L'implémentation n'est pas accessible à l'utilisateur. 2 classes ayant la même interface peuvent avoir une implémentation différente.
La classe est le "moule" à partir duquel on crée un objet. Un objet en est une instanciation.
On peut distinguer les objets entre eux en les nommant, même s'ils sont de la même classe.
class NomDeLaClasse
{
public:
int uneFonctionMembre();
double unAttribut;
private:
char unAutreAttribut;
}; // ⚠️ Do not forget the semicolon
class XYZ
{
public:
void fctPub(){..}
int varPub;
private:
void fctPriv(){..}
int varPriv;
};
int main()
{
XYZ x;
x.fctPub(); // ✅
x.varPub=3; // ✅
x.fctPriv(); // ⛔
x.varPriv=6; // ⛔
}
class XYZ
{
public:
void fctPub(){..}
int varPub;
private:
void fctPriv(){..}
int varPriv;
};
void XYZ::fctPub()
{
varPub = 3; // ✅
varPriv = 6; // ✅
}
void XYZ::fctPriv()
{
varPub = 3; // ✅
varPriv = 6; // ✅
}
L'implémentation et le type des données sont cachés (peuvent évoluer)
⇒ Permet
La visibilité des membres est gérée avec
L'accès aux membres est géré par des
⇒ Garantit la cohérence/sécurité des données
Les fonctions sont
class Account
{
public:
void deposit(int x)
{
balance += x;
}
void withdraw(int x)
{
balance -= x;
}
int getBalance()
{
return balance;
}
private:
int balance{0};
};
int main()
{
Account myAccount;
myAccount.deposit(100);
myAccount.withdraw(50);
auto balance = myAccount.getBalance();
std::println("Balance: {}", balance);
myAccount.balance = 1000; // ⛔
std::println("Balance: {}", myAccount.balance); // ⛔
return 0;
}
Certaines méthodes doivent être publiques
les constructeurs (en général)
les accesseurs, les modificateurs
On recommande de
déclarer les attributs privés
les rendre accessibles par des méthodes publiques (accesseurs et modificateurs).
📌 Note : un membre déclaré sans modificateur d'accès sera
Dans la déclaration de la classe
Mot-clef inline (implicite)
Pour des fonctions courtes
Prototype dans déclaration de la classe
Définition hors de la déclaration de la classe
Opérateur de portée
class Point
{
public:
int getX()
{
return x;
}
inline void setX(int _x) // 📌 inline is optional here
{
x = _x;
}
private:
int x{0}, y{0};
};
// Class declaration
class Point
{
public:
void display();
private:
int x{0}, y{0};
};
// Point::display() definition
inline void Point::display() // 📌 inline mandatory
{
std::println("({},{})", x, y);
}
2 fichiers par classe :
Directives de non-inclusion multiple dans les interfaces
// Classe.h
#ifndef CLASSE_H
#define CLASSE_H
...
#endif
// Classe.cpp
#include "classe.h"
// Classe.h
#pragma once
...
// Classe.cpp
#include "classe.h"
📌 1 fichier peut contenir plusieurs classes
✅ 1 seule classe par paire de fichiers
// Point.h
#pragma once
class Point
{
public:
...
int getX(){return x;} // inline
void print();
};
// Point.cpp
#include "Point.h"
...
void Point::print()
{
std::println("Point: ({},{})", x, y);
}
// main.cpp
#include "Point.h"
int main()
{
Point ptA;
...
return 0;
}
Instancier : créer un objet à partir d'une classe
Chaque objet dispose de son propre jeu d'attributs
La syntaxe est la même que pour déclarer des variables
Instanciation automatique
int main()
{
// Allocated on the stack
Point p1;
...
p1.print();
...
}
Instanciation dynamique
int main()
{
// Allocated on the heap
auto *p = new Point;
...
p->print();
...
delete p;
p = nullptr;
}
Un type est utilisé pour
Une classe est utilisée pour
Les attributs et les méthodes sont relatifs à un objet : il est nécessaire de commencer par instancier un objet à partir d'une classe avant de pouvoir accéder à ses membres. C'est pour cette raison que les membres d'une classe sont parfois appelés attributs d'instance ou méthodes d'instance.
Problème
Entre le moment où un objet est instancié et son initialisation, l'état interne est
Point p; // ⚠️ p state is unknown
p.print(); // ⚠️ (?, ?)
p.init(3,5); // Initialisation
p.print(); // ✅ (3,5)
Initialiser un objet lors de son instanciation
Instanciation PUIS initialisation
Point p;
p.init(3,5);
Instanciation ET initialisation
On peut initialiser l'état interne d'un objet lors de son instanciation grâce à un constructeur (méthode spéciale)
Point p(3,5);
Quand on instancie un objet :
1) La mémoire nécessaire est allouée
2) Le constructeur de la classe est automatiquement appelé pour initialiser les attributs
Un constructeur est une méthode avec les caractéristiques suivantes:
Son nom est celui de la classe
Il ne contient pas d'instruction
Il ne possède pas de type de retour (même pas
class Point
{
public:
Point()
{
x = 0;
y = 0;
}
private:
int x, y;
};
1) ✅ Quand c'est possible, on initialise directement les attributs de l'objet
2) Ce constructeur devient alors le même que le constructeur par défaut
class Point
{
public:
Point()=default; // 📌 Optional, but clearer
private:
int x{0}, y{0};
};
Un constructeur peut avoir des paramètres et peut être
2 façons de définir un constructeur par défaut (sans paramètres)
class Point
{
public:
Point()
{
x = 0;
y = 0;
}
...
};
class Point
{
public:
Point(int a=0, int b=0);
...
};
Point::Point(int a, int b)
{
x=a; y=b;
}
Le constructeur par défaut est appelé chaque fois qu'un objet doit être créé sans arguments
Le compilateur fournit un constructeur par défaut, si le programmeur n'a écrit aucun autre constructeur
Le constructeur par défaut proposé par le compilateur ne fait rien:
auto x = Point();
Point y;
// ⚠️ Ne jamais mettre de parenthèses !
// Sinon, c'est une déclaration de fonction appelée z
// Point z();
Point t[10]; // 10 appels de Point()
auto p = new Point; // équiv.à: p = new Point()
auto q = new Point[10]; // 10 appels de Point()
Un constructeur standard est un constructeur qui prend des paramètres
class Point
{
public:
Point(int a, int b);
...
};
Point::Point(int a, int b)
{
x=a; y=b;
}
class Point
{
public:
// ⚠️ constructeur par défaut !
Point(int a=0, int b=0);
...
};
Point::Point(int a, int b)
{
x=a; y=b;
}
Un constructeur standard est appelé lorsqu'un objet doit être créé avec des arguments
Point p1(1, 2);
auto p2 = Point(1, 2);
// ⚠️ Use default CTor
Point p3[10];
// ⚠️ Use Point(int, int), then use default CTor
Point p4[4] = {Point(5, 3)};
auto p5 = new Point(6, 3);
delete p5;
auto p6 = new Point[10];
for (int i = 0; i < 10; i++)
{
p6[i] = Point(2,3);
}
delete[] p6;
std::vector<Point> v(10, Point(2,3));
Le pointeur
Point::Point(int x, int y)
{
this->x = x;
this->y = y;
}
1) Éviter le masquage de l'attribut par un paramètre
2) Améliorer la lisibilité du code source: on distingue mieux les méthodes/attributs de l'objet courant et les fonctions globales
3) Comparer l'adresse de l'objet courant avec celle d'un autre objet.
Un seul argument obligatoire, d'un autre type simple
class Point
{
public:
// Conversion constructor from int
Point(int a) {x = y = a;}
...
};
Exemples
auto p1 = Point(2);
Point p2(3);
Point p3 = 4; // ⚠️ Implicit conversion
p3 = 5; // ⚠️ Implicit conversion
Forcer toutes les conversions à être explicites :
class Point
{
public:
// Conversion constructor from int
explicit Point(int a) {x = y = a;}
...
};
Exemples
auto p1 = Point(2); // ✅ Explicit conversion
Point p2(3); // ✅ Explicit conversion
Point p3 = 4; // ⛔ Implicit conversion
p3 = 5; // ⛔ Implicit conversion
Un constructeur par copie est un constructeur qui prend en paramètre une référence constante sur un objet de la classe
class Point
{
public:
Point(const Point &p) {x = p.x; y = p.y;}
...
};
Exemples
Point p1(2,3);
Point p2(p1); // Copy constructor
auto p3 = p1; // Copy constructor
Ce constructeur est souvent appelé implicitement
1) À l'initialisation d'un objet, avec l'opérateur =
2) Lorsqu'on passe un objet en paramètre par valeur à une fonction
3)
Le compilateur propose automatiquement un constructeur par copie trivial (bit à bit). On parle de
⚠️ La copie en surface ne fonctionne pas si l'objet contient des pointeurs ou des références.
Il est souvent nécessaire de redéfinir un constructeur par copie pour réaliser une
Problème : 1 ou plusieurs des attributs sont des pointeurs (ou similaires)
class Point
{
public:
...
Point(const Point& point) = default;
// 📌 Equivalent to:
// Point(const Point &point)
// {
// this->x = point.x;
// this->y = point.y;
// this->name = point.name;
// }
...
void setName(const char *name)
{
strcpy(this->name, name);
}
private:
...
char *name = nullptr;
};
Problème : 1 ou plusieurs des attributs sont des pointeurs (ou similaires)
int main()
{
auto p1 = Point(1, 2, "P1");
// ⚠️ Shallow copy
auto p2 = p1;
// ⚠️ p1 is modified too
p2.setName("P2");
return 0;
}
Solution : copie en profondeur
class Point
{
public:
...
Point(int x, int y, const char *name = "")
{
this->x = x;
this->y = y;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
// ✅ Deep copy: allocate new memory, copy content
Point(const Point &other) : x(other.x), y(other.y)
{
this->name = new char[strlen(other.name) + 1];
strcpy(this->name, other.name);
}
private:
...
char *name = nullptr;
};
auto a = Point(1, 1);
Point b(2, 2);
Point c = 3; // ⛔ Implicit conversion
auto d = new Point(4, 4);
Point e;
Point f[4];
Initialisation d'objets temporaires, anonymes
std::println("Distance: {}", distance(Point(10,0), Point(3, 4)));
2 interprétations différentes pour le
// A)
auto p = Point(1, 2);
// B)
Point p;
p = Point(1, 2);
A) 1 appel au constructeur standard
Cette expression crée l'objet p et l'initialise en rangeant dans p.x et p.y les valeurs 1 et 2
2 interprétations différentes pour le
// A)
auto p = Point(1, 2);
// B)
Point p;
p = Point(1, 2);
B) 2 appels de constructeurs + 1 affectation
- crée un objet p (constructeur par défaut)
- crée un point anonyme de coordonnées (1,2)
- affecte les valeurs des attributs du point anonyme aux attributs de p
⚠️ Risque si l'opérateur
class Point
{
public:
Point(int a, int b) : x(a), y(b) {}
...
};
Une liste d'initialisation est indispensable pour :
1) initialiser un
2) initialiser un
3) initialiser un
Valable pour tous les constructeurs
✅ À utiliser autant que possible
⚠️ Le constructeur par défaut fourni par le compilateur
Si un constructeur a des valeurs par défaut pour ses paramètres, celles-ci doivent figurer dans la
déclaration de la classe (fichier d'interface :
⚠️ Il faut redéfinir le constructeur par copie quand une copie en profondeur est nécessaire
La déclaration d'un tableau d'objets génère autant d'appels au constructeur par défaut que d'éléments dans le tableau
class Point
{
public:
...
~Point() {...}
...
};
1 seul destructeur
Destructeur par défaut généré par le compilateur (trivial)
Permet au programmeur de détruire proprement un objet (libérer la mémoire, fermer les flux, stopper les threads, etc.)
class PointN
{
public:
~PointN();
};
PointN::~PointN()
{
delete [] nom;
}
int main()
{
auto *p = new PointN;
...
delete p;
...
auto *q = new PointN[10];
...
delete [] q;
}
Le destructeur est appelé automatiquement lors de la destruction d'un objet
Un attribut d'une classe peut être qualifié
Initialisé lors de la construction d'un objet
Ne peut plus être modifié ensuite
class Segment
{
public:
Segment(int x,int y, int num);
...
private:
const int ID{0}; // 1) ✅ OK
};
Segment::Segment(..., int num){..., ID = num;} // ⛔ Error
...
Segment::Segment(..., int num) : ID(num){...} // ou 2) ✅ OK
Le mot-clé
C'est une méthode de
class Point
{
// Can modify the object
void setXY(float newX, float newY);
// Cannot modify the object
float getX( ) const;
};
const Point pt = ...;
auto x = pt.getX();
⇒ la qualification
Le mot-clef
⇒ On peut surcharger une méthode non-constante par une méthode constante
La fonction non constante sera appelée par les objets variables, la fonction
class Point
{
int x,y;
public:
int X() const {return x;}
int &X() {return x;}
};
const Point pC(2, 3);
int r;
...
r = pC.X(); // ✅ OK
pC.X() = 15; // ⛔ Error: rvalue
Point p(4,5);
int r;
...
r = p.X(); // ✅ OK
p.X() = 15; // ✅ OK: lvalue
class Point
{
int x,y;
public:
int &X() const {return x;} // ⛔ Error
};
Comme
Le pointeur
📌 Les méthodes sont toujours appelées depuis un objet
⇒ L'adresse de l'objet est implicitement passée à la méthode appelée
⚠️
Le déréférencement de
1) Éviter le masquage de l'attribut par un paramètre :
Point::Point(int x, int y)
{
this->x = x;
this->y = y;
}
2) Améliorer la lisibilité du code source : on distingue mieux les membres de l'objet courant des paramètres
3) Comparer l'adresse de l'objet courant avec celle d'un autre objet
A) Membres d'instance (cas connu)
- Chaque objet d'une classe a ses propres attributs
- Les méthodes sont appelées à partir des objets d'une classe
B) Membres de classe
- Un attribut de classe est unique pour tous les objets d'une classe
- Une méthode de classe peut être appelée à partir de la classe
class Point
{
public:
...
static int getNbPoints() {return nbPoints;}
private:
int x{0}, y{0}; // Unique to each object
static int nbPoints; // Shared by all objects
};
⚠️ Un attribut de classe (
int Point::nbPoints = 0;
int main()
{
...
std::println("Nombre de points: {}", Point::getNbPoints());
}
⚠️ Une méthode statique ne peut accéder qu'aux membres statiques
class Point
{
public:
...
static int GetX() {return x;} // ⛔ Error
};
⚠️ Une méthode statique ne dispose pas du pointeur
📌 Les méthodes statiques sont utilisables sans instancier un objet
Il est possible de passer outre la protection d'accès aux membres privés d'une classe grâce au mot-clé
1) Une fonction amie (
2) Une classe amie (
1) Une fonction amie doit être déclarée dans la classe qui accorde le droit d'accès
class Point
{
...
public:
friend void fctExt(Point &p);
...
};
void fctExt(Point &p)
{
p.x = 0; // ✅ OK: fctExt is a friend of Point
}
int main()
{
Point p;
p.x = 0; // ⛔ Error: x is private
fctExt(p); // ✅ OK: fctExt is a friend of Point
}
2) Une classe amie doit être déclarée dans la classe hôte
class Host
{
friend class ZeFriend;
int x;
public:
Host(int a):x(a){}
};
class ZeFriend
{
public:
void displayHost()
{
Host h(16);
// ✅ OK: ZeFriend is a friend of Host
std::println("Host x = {}", h.x);
}
};
📌 Fonctionne aussi pour le constructeur par copie, et le destructeur
class A
{
public:
A()=default;
A(const A&) = delete; // No copy allowed
~A()=default;
};
Un constructeur peut appeler un autre constructeur avec des paramètres différents
class A
{
public:
A()=default;
A(char x, int y) {}
A(int y) : A('a', y) {}
};