Vous êtes sur la page 1sur 42

Bases de la

programmation
orientée objet
Laurent SARRY
laurent.sarry@uca.fr
Quizz Wooclap #10
Principe de la POO
• La programmation orientée objet (POO) offre un mécanisme
de classes réutilisables qui rassemblent des données et des
traitements sur ces données
• Elle favorise la modularité, la lisibilité et la réutilisabilité
• Elle introduit de nouveaux concepts comme l’héritage,
l’encapsulation, l’abstraction et le polymorphisme
Classes & membres de classe
• Les classes sont une généralisation des structures qui
permettent de stocker des données hétérogènes
struct Date
{
int jour {};
int mois {};
int annee {};
};
int main()
{
Date date{ 12, 01, 2022 };
date.jour = 13;
return 0;
}
Classes & membres de classe
• Les classes sont une généralisation des structures qui
permettent de stocker des données hétérogènes
class Date
{
public:
int m_jour {};
int m_mois {};
int m_annee {};
};
int main()
{
Date date{ 12, 01, 2022 };
date.m_jour = 13;
return 0;
}
Classes & membres de classe
• En plus des données, une classe contient des fonctions
membre qui ont accès aux données
class Date int main()
{ {
public: Date date{ 12, 01, 2022 };
int m_jour {}; date.print();
return 0;
int m_mois {};
}
int m_annee {};
void print()
{
std::cout << m_jour << '/' << m_mois << '/' << m_annee << '\';
}
};
Classes & membres de classe
• Types imbriqués : il est possible de définir des alias de type à
l’intérieur d’une classe
• La classe joue alors le rôle d’un espace de nommage
class Calculatrice int main()
{ {
public: Calculatrice calculatrice;
using nombre_type = int;
std::cout << calculatrice.addition(3, 5) << '\n'; // 8
std::vector<nombre_type> m_Historique{}; std::cout << calculatrice.addition(23, 45) << '\n'; // 68
nombre_type addition(nombre_type a, nombre_type b) for (Calculatrice::nombre_type resultat : calculatrice.m_Historique)
{ {
auto resultat{ a + b }; std::cout << resultat << '\n';
}
m_Historique.push_back(resultat);
return 0;
return resultat; }
}
};
Spécificateurs d’accès
• Par défaut les membres d’une classe sont privés, c’est-à-dire
qu’ils ne sont accessibles que par les fonctions membres
class Date
{
int m_jour {}; int main()
int m_mois {}; {
int m_annee {}; Date date;
public: date.setDate(12, 01, 2022);
// public, peut être accédé par n'importe qui return 0;
void setDate(int jour, int mois, int annee) }
{
// peut accéder aux membres privés car fait partie de la classe
m_jour = jour;
m_mois = mois;
m_annee = annee;
}
};
Fonctions d’accès et encapsulation
• Il est préférable de ne pas pouvoir accéder directement aux
membres des classes, mais de passer par des fonctions d’accès
• L’implémentation exacte des données n’est pas connue, seule
l’interface publique est connue pour utiliser la classe
• Ne pas accéder directement aux membres permet de protéger les
données et d’éviter les erreurs d’accès mémoire
• L’encapsulation facilite l’évolution du code et son débogage
Fonctions d’accès et encapsulation
• Les fonctions d’accès sont de deux types :
• Getter (accesseur) : retourne la valeur ou une référence constante à
une variable membre privée
• Setter (mutateur) : modifie la valeur d’une variable membre privée
class Date int getMois() { return m_mois; } // getter pour mois
{ void setMois(int mois) { m_mois = mois; } // setter pour mois
private:
int m_jour {}; int getAnnee() { return m_annee; } // getter pour annee
int m_mois {}; void setAnnee(int annee) { m_annee = annee; } // setter pour annee
int m_annee {}; };

public:
int getJour() { return m_jour; } // getter pour jour
void setJour(int jour) { m_jour = jour; } // setter pour jour
Question #1
Constructeurs
• L’initialisation des variables membres par liste n’est possible
que si elles sont publiques, sinon il faut un constructeur
• Un constructeur est une fonction membre qui est appelée
automatiquement à la création de la classe
class Fraction
int main()
{
{
private:
// initialisation des membres à zéro
int m_numerateur {};
// puis appel du constructeur par défaut Fraction()
int m_denominateur {};
Fraction frac1{};
public:
// appel du constructeur par défaut Fraction()
Fraction() // constructeur par défaut
Fraction frac2;
{
return 0;
m_numerateur = 0;
}
m_denominateur = 1;
}
};
Constructeurs
• Il est possible de définir des constructeurs avec paramètres
(surcharge de fonction membre)
// Constructeur avec deux paramètres dont un par défaut
Fraction(int numerateur, int denominateur=1)
{
assert(denominateur != 0);

m_numerateur = numerateur;
m_denominateur = denominateur;
}
Fraction cinqTiers{5, 3}; // initialisation par liste, appel de Fraction(5, 3)
Fraction troisQuarts(3, 4); // initialisation directe, appel de Fraction(3, 4)
Fraction six{ 6 }; // initialisation par liste, appel de Fraction(6, 1)
Constructeurs
• En pratique il faut limiter le plus possible le nombre de
constructeurs
// constructeur par défaut et avec paramètres
Fraction(int numerateur=0, int denominateur=1)
{
assert(denominateur != 0);

m_numerateur = numerateur;
m_denominateur = denominateur;
}

Fraction zero; // appel de Fraction(0, 1)


Fraction zero{ }; // appel de Fraction(0, 1)
Fraction six{ 6 }; // appel de Fraction(6, 1)
Fraction cinqTiers{5, 3}; // appel de Fraction(5, 3)
Constructeurs
• Constructeur implicite : si la classe n’a pas de constructeur,
C++ génère un constructeur public par défaut
• s’il existe un autre constructeur, le constructeur par défaut doit être
défini par l’utilisateur int main()
{
class Date
Date date{}; // initialisation au 1er janvier 1900
{
Date today{ 12, 01, 2022 }; // initialisation au 12 janvier 2022
int m_jour{ 1 };
int m_mois{ 1 };
return 0;
int m_annee{ 1900 };
}
public:
// force la création d'un constructeur par défaut
// malgré l'existence d'un autre constructeur
Date() = default;
Date(int jour, int mois, int annee)
{
m_jour = jour;
m_mois = mois;
m_annee = annee;
}
};
Constructeurs
• Liste d’initialisation de membres : à préférer plutôt qu’une
affectation des membres dans le corps du constructeur
Date(int jour, int mois, int annee)
: m_jour{ jour }, m_mois{ mois }, m_annee{ annee }
{

}
Constructeurs
• Initialisation de variables membres qui sont des classes :
class A
{
public:
A(int x = 0) { std::cout << "A " << x << '\n'; }
};
int main()
class B {
{ B b{ 5 }; // Affiche A 4 puis B 5
private: return 0;
A m_a {};
}
public:
B(int y)
: m_a{ y - 1 } // appel de A(y - 1) pour initialiser m_a
{
std::cout << "B " << y << '\n';
}
};
Question #2
Question #3
Question #4
Question #5
Question #6
Initialisation de membres non statiques
• Il est préférable de donner une valeur par défaut aux membres
non statiques, plutôt que de le faire dans les différents
constructeurs
class Fraction
{ int main()
private: {
int m_numerateur { 0 }; Fraction cinqTiers{5, 3};
int m_denominateur { 1 };
Fraction six{ 6 };
return 0;
public:
}
Fraction(int numerateur, int denominateur)
: m_numerateur{ numerateur }, m_denominateur{ denominateur }
{
// m_numerateur et m_denominateur initialisées par le constructeur
}
Fraction(int numerateur)
: m_numerateur{ numerateur }
{
// m_numerateur initialisée par le constructeur
// la valeur 1 par défaut de m_denominateur est utilisée
}
};
Délégation de constructeur
• Si les constructeurs d’une classe sont redondants
• Pour éviter la duplication de code, il est possible d’appeler un
constructeur dans un autre, c’est la délégation de constructeur
class Exemple
{
private:
int m_a{ 2 };
public:
Exemple()
{
}
Exemple(int value) : Exemple{} // appel le constructeur par défaut
{
}
};
Délégation de constructeur
• Un constructeur ne peut pas à la fois initialiser des membres
et déléguer à un autre constructeur
• Pour faire les deux, il est possible d’appeler une fonction membre
standard dans les constructeurs qui sera chargée des initialisations
• Pour réinitialiser les membres d’une classe et éviter d’avoir
des initialisations à deux endroits dans le code
• il est possible de créer une fonction membre qui appelle le
constructeur par défaut
void reset()
{
*this = Exemple();
}
Question #7
Destructeurs
• Un destructeur est un type particulier de fonction membre qui
est appelée à la destruction de la classe
• Utile si la classe comporte des membres alloués dynamiquement,
sinon si la classe se limite à initialiser des variables normales, la
mémoire sera libérée automatiquement
• Le nom du destructeur est le même que celui de la classe précédé par
tilde ~, le destructeur ne prend pas d’argument et ne retourne rien
Destructeurs
class IntTableau
{
private:
int* m_tableau{};
int m_longueur{};
public:
IntTableau(int longueur) // constructeur
{
m_tableau = new int[static_cast<std::size_t>(longueur)]{};
m_longueur = longueur;
}
~IntTableau() // destructeur
{
delete[] m_tableau;
}
};
int main()
{
IntTableau tab ( 10 ); // allocation de 10 entiers
return 0;
} // tab est détruit ici, et le destructeur ~IntTableau est appelé
Question #8
Pointeur « this »
• Le pointeur this est un pointeur const sur l’objet en cours
• Il peut être utilisé pour faire la distinction entre des variables
membre et des paramètres de même nom, mais il est préférable de
différentier le nom des membres, par exemple avec le préfixe m_
class Exemple
{
private:
int donnee;
public:
Exemple(int donnee)
{
// this->donnee est membre, donnee est paramètre local
this->donnee = donnee;
}
};
Classes et fichiers d’en-tête
• Pour les classes complexes, il faut séparer la définition (.h) et
l’implémentation (.cpp) des fonctions membres
#ifndef DATE_H // Fichier Date.cpp
#define DATE_H #include "Date.h"
class Date
// préfixe avec le nom de classe
{
// et l'opérateur de résolution de portée ::
int m_jour{ 1 }; Date::Date(int jour, int mois, int annee)
int m_mois{ 1 }; {
int m_annee{ 1900 }; SetDate(jour, mois, annee);
public: }
Date() = default;
void Date::SetDate(int jour, int mois, int annee)
Date(int jour, int mois, int annee);
{
void SetDate(int jour, int mois, int annee); m_jour = jour;
int getJour() { return m_jour; } m_mois = mois;
int getMois() { return m_mois; } m_annee = annee;
int getAnnee() { return m_annee; } }
};
#endif
Objets const et fonctions membres
• Nous avons vu qu’il était conseillé de passer les objets par
référence constante comme paramètres de fonction afin
d’éviter une copie de l’objet en mémoire
• Cependant, pour que l’objet const en argument puisse appeler les
fonctions membres de la classe, ces dernières ne doivent pas pouvoir
modifier les membres de la classe et donc être définies const
Objets const et fonctions membres
class Exemple
{
private:
int m_donnee{};

public:
Exemple(int donnee) : m_donnee(donnee) { }
int getDonnee() const { return m_donnee;}
};
void Affiche(const Exemple& exemple)
{
std::cout << exemple.getDonnee() << '\';
}

int main()
{
Exemple exemple(1);
Affiche(exemple);
return 0;
}
Variables membres statiques
• Les variables membres statiques sont partagées par tous les
objets de la classe
• Il est préférable d’y accéder par le nom de classe avec l’opérateur de
résolution de portée, plutôt que par un objet de la classe
class Exemple int main()
{ {
public: Exemple ex1, ex2;
// déclaration de la variable statique ex1.s_donnee = 2;
static int s_donnee; std::cout << ex1.s_donnee; // affiche 2
}; std::cout << ex2.s_donnee; // affiche 2
std::cout << Exemple::s_donnee; // affiche 2
// définition de la variable statique return 0;
// en général dans le .cpp }
int Exemple::s_donnee{ 1 };
Question #9
Fonctions membres statiques
• Les fonctions membres statiques permettent d’accéder aux
variables membres statiques privées auxquelles on ne peut
accéder directement
class Exemple
{
private:
static int s_donnee;
public:
static int getDonnee();
};

int Exemple::s_donnee{ 1 };
// la fonction membre statique peut être définie à l'extérieur de la classe
int Exemple::getDonnee() { return s_donnee; }

int main()
{
std::cout << Exemple::getDonnee(); // affiche 1
return 0;
}
Fonctions et classes amies
• Une fonction amie est une fonction qui a accès aux membres
privés d’une classe sans être membre
class Exemple bool estEgal(const Exemple& exemple1, const Exemple& exemple2)
{ {
private: return (exemple1.m_donnee == exemple2.m_donnee);
int m_donnee{}; }

public: int main()


Exemple(int donnee) : m_donnee(donnee) {
{ Exemple ex1{ 5 };
} Exemple ex2{ 6 };
friend bool estEgal(const Exemple& exemple1, std::cout << std::boolalpha << estEgal(ex1, ex2);
const Exemple& exemple2); return 0;
}; }
Fonctions et classes amies
• Une classe amie est une classe qui a accès aux membres privés
de la classe
class Affiche
class Affiche; // déclaration anticipée
{
public:
class Exemple
void afficheExemple(const Exemple& exemple)
{
{
private:
std::cout << exemple.m_donnee << '\';
int m_donnee{};
}
};
public:
Exemple(int donnee) : m_donnee(donnee)
int main()
{
{
}
Exemple exemple(5);
friend Affiche;
Affiche affiche;
};
affiche.afficheExemple(exemple);
return 0;
}
Fonctions et classes amies
• Plutôt que de rendre une classe amie en entier, il est possible
de se limiter à une fonction membre particulière
class Exemple; // déclaration anticipée void Affiche::afficheExemple(const Exemple& exemple)
class Affiche {
{ std::cout << exemple.m_donnee << '\';
public: }
void afficheExemple(const Exemple& exemple);
}; int main()
class Exemple {
{ Exemple exemple(5);
private: Affiche affiche;
int m_donnee{}; affiche.afficheExemple(exemple);
return 0;
public: }
Exemple(int donnee) : m_donnee(donnee)
{
}
friend void Affiche::afficheExemple(const Exemple& exemple);
};
Question #10
Question #11
Types imbriqués dans une classe
• Les types imbriqués peuvent être membre de classe
class Fruit int main()
{ {
public: Fruit pomme { Fruit::pomme };
enum TypeFruit
{ if (pomme.getType() == Fruit::pomme)
pomme, std::cout << "Pomme";
banane, else
cerise std::cout << "Pas une pomme";
};
private: return 0;
TypeFruit m_type{}; }
public:
Fruit(TypeFruit type) : m_type { type } { }
TypeFruit getType() const { return m_type; }
};

Vous aimerez peut-être aussi