Héritage, constructeurs et polymorphisme en C++
4 générations
Résumé
I. Héritage
- Définition : Mécanisme POO créant une classe à partir d'une existante, héritant attributs, méthodes, comportement.
- But : Réutilisation code, spécialisation, hiérarchie logique.
- Terminologie : Classe mère (base), classe fille (dérivée, sous-classe).
- Exemple :
Animal(mère) ->Chien(fille). - Règles d'accès :
- Membres
privateclasse mère : Inaccessibles par classe fille. - Membres
protectedclasse mère : Accessibles par classe fille, inaccessibles de l'extérieur.
- Membres
- Types d'héritage :
public: Maintien niveau protection.protected: Public/protected mère deviennent protected fille.private: Public/protected mère deviennent private fille.
II. Héritage et constructeurs
- Appel en cascade : Création objet dérivé -> appel constructeur mère -> appel constructeur fille.
- Initialisation : Attributs hérités initialisés par constructeur mère.
- Choix constructeur mère : Possible via liste d'initialisation (ex:
: Personne(n, p, a)). - Ordre construction : Hiérarchique, de la classe la plus générale à la plus spécifique (ex:
Animal->Chien->Chiwawa).
III. Masquage
- Définition : Méthode classe fille même nom que méthode classe mère.
- Conséquence : Méthode mère cachée, méthode fille appelée pour objets dérivés.
- Appel : Dépend du type statique de la variable (pas type réel objet) si méthode non virtuelle.
- Raison : Personnaliser comportement pour classe fille.
- Bonne pratique : Appeler méthode mère (
ClasseMere::methode()) puis ajouter spécificités fille pour éviter duplication code.
IV. Polymorphisme
- Problématique : Stocker différents types d'objets (ex:
Personne,Etudiant) dans même conteneur (ex:vector<Personne*>). - Observation :
Etudiantest unePersonne,Etudiant*peut être stocké dansPersonne*. - Problème sans polymorphisme : Appel méthode non virtuelle (ex:
afficher()) basé sur type statique du pointeur (Personne*), pas type réel objet. - Solution : Déclarer méthode comme
virtualdans classe mère (virtual void afficher();). - Résultat avec polymorphisme : Appel méthode dépend du type réel de l'objet pointé.
V. Conteneurs et parcours par itérateur
- Itérateurs :
- Utilité : Flexibilité, parcours tous conteneurs STL, interaction précise éléments, utilisation algorithmes génériques.
- Exemple :
vector<int>::iterator it; for (it = v.begin(); it != v.end(); it++).
- Conteneurs STL (vue d'ensemble) :
- Séquence :
vector(dynamique, accès index),list(chaînée). - Associatifs :
set(uniques),map(clé/valeur). - Adaptateurs :
stack,queue,priority_queue.
- Séquence :
- Bibliothèque
<algorithm>:- Fonctions :
sort,find,count,copy, etc. - Utilisation : Avec tous conteneurs via itérateurs (ex:
sort(v.begin(), v.end());).
- Fonctions :
**I. Héritage**
* **Définition :** Mécanisme POO créant une classe à partir d'une existante, héritant attributs, méthodes, comportement.
* **But :** Réutilisation code, spécialisation, hiérarchie logique.
* **Terminologie :** Classe mère (base), classe fille (dérivée, sous-classe).
* **Exemple :** `Animal` (mère) -> `Chien` (fille).
* **Règles d'accès :**
* Membres `private` classe mère : Inaccessibles par classe fille.
* Membres `protected` classe mère : Accessibles par classe fille, inaccessibles de l'extérieur.
* **Types d'héritage :**
* `public` : Maintien niveau protection.
* `protected` : Public/protected mère deviennent protected fille.
* `private` : Public/protected mère deviennent private fille.
**II. Héritage et constructeurs**
* **Appel en cascade :** Création objet dérivé -> appel constructeur mère -> appel constructeur fille.
* **Initialisation :** Attributs hérités initialisés par constructeur mère.
* **Choix constructeur mère :** Possible via liste d'initialisation (ex: `: Personne(n, p, a)`).
* **Ordre construction :** Hiérarchique, de la classe la plus générale à la plus spécifique (ex: `Animal` -> `Chien` -> `Chiwawa`).
**III. Masquage**
* **Définition :** Méthode classe fille même nom que méthode classe mère.
* **Conséquence :** Méthode mère cachée, méthode fille appelée pour objets dérivés.
* **Appel :** Dépend du type statique de la variable (pas type réel objet) si méthode non virtuelle.
* **Raison :** Personnaliser comportement pour classe fille.
* **Bonne pratique :** Appeler méthode mère (`ClasseMere::methode()`) puis ajouter spécificités fille pour éviter duplication code.
**IV. Polymorphisme**
* **Problématique :** Stocker différents types d'objets (ex: `Personne`, `Etudiant`) dans même conteneur (ex: `vector<Personne*>`).
* **Observation :** `Etudiant` est une `Personne`, `Etudiant*` peut être stocké dans `Personne*`.
* **Problème sans polymorphisme :** Appel méthode non virtuelle (ex: `afficher()`) basé sur type statique du pointeur (`Personne*`), pas type réel objet.
* **Solution :** Déclarer méthode comme `virtual` dans classe mère (`virtual void afficher();`).
* **Résultat avec polymorphisme :** Appel méthode dépend du type réel de l'objet pointé.
**V. Conteneurs et parcours par itérateur**
* **Itérateurs :**
* **Utilité :** Flexibilité, parcours tous conteneurs STL, interaction précise éléments, utilisation algorithmes génériques.
* **Exemple :** `vector<int>::iterator it; for (it = v.begin(); it != v.end(); it++)`.
* **Conteneurs STL (vue d'ensemble) :**
* **Séquence :** `vector` (dynamique, accès index), `list` (chaînée).
* **Associatifs :** `set` (uniques), `map` (clé/valeur).
* **Adaptateurs :** `stack`, `queue`, `priority_queue`.
* **Bibliothèque `<algorithm>` :**
* **Fonctions :** `sort`, `find`, `count`, `copy`, etc.
* **Utilisation :** Avec tous conteneurs via itérateurs (ex: `sort(v.begin(), v.end());`).# 🧠 Fiche de Révision : Héritage et Polymorphisme en POO
---
## 🔄 **1. Qu’est-ce que l’héritage en POO ?**
- **Définition** : Mécanisme permettant de créer une **classe fille** à partir d’une **classe mère**, en héritant de ses **attributs**, **méthodes** et **comportement**.
- **But** :
- **Réutilisation** du code.
- **Spécialisation** des classes.
- Création d’une **hiérarchie logique**.
### 📚 **Terminologie clé**
| Terme | Définition |
|----------------|-------------------------------------|
| **Classe mère** | Classe de base (ex: `Animal`). |
| **Classe fille**| Classe dérivée (ex: `Chien`). |
---
## 🔐 **2. Comment gérer l’accès aux membres hérités ?**
### ⚠️ **Règles d’accès**
- **`private`** : Inaccessible depuis la **classe fille**.
- **`protected`** : Accessible **uniquement** par la classe fille (pas depuis l’extérieur).
- **`public`** : Accessible partout.
### 🔧 **Types d’héritage**
| Type | Effet sur les membres de la classe mère |
|---------------|-----------------------------------------|
| **`public`** | Maintient leur niveau de protection. |
| **`protected`**| `public`/`protected` → `protected`. |
| **`private`** | Tous les membres → `private`. |
---
## 🏗️ **3. Comment fonctionnent les constructeurs en héritage ?**
### 🔄 **Ordre d’appel**
1. **Constructeur de la classe mère** (ex: `Animal`).
2. **Constructeur de la classe fille** (ex: `Chien`).
3. **Constructeur de la classe la plus spécifique** (ex: `Chiwawa`).
### 🛠️ **Initialisation des attributs**
- Les **attributs hérités** sont initialisés par le **constructeur de la classe mère**.
- **Choix du constructeur mère** : Via la **liste d’initialisation**.
```cpp
Chien::Chien(string n, string p, int a) : Animal(n, p, a) {}
```
---
## 🎭 **4. Qu’est-ce que le masquage en POO ?**
- **Définition** : Une **méthode de la classe fille** porte le **même nom** qu’une méthode de la classe mère.
- **Conséquence** : La méthode mère est **cachée** pour les objets de la classe fille.
### 💡 **Bonne pratique**
- **Appeler la méthode mère** avant d’ajouter des spécificités :
```cpp
void Chien::afficher() {
Animal::afficher(); // Appel méthode mère
cout << "Je suis un chien !"; // Spécificités fille
}
```
---
## 🔄 **5. Qu’est-ce que le polymorphisme ?**
### ❓ **Problématique**
- Stocker des **objets de types différents** (ex: `Personne`, `Etudiant`) dans un **même conteneur** (ex: `vector<Personne*>`).
- **Sans polymorphisme** : L’appel d’une méthode dépend du **type statique** du pointeur (ex: `Personne*`), pas du type réel de l’objet.
### ✅ **Solution**
- Déclarer la méthode comme **`virtual`** dans la classe mère :
```cpp
virtual void afficher();
```
- **Résultat** : L’appel dépend du **type réel** de l’objet pointé.
---
## 📦 **6. Conteneurs STL et itérateurs**
### 🔄 **Itérateurs**
- **Utilité** :
- Parcours **flexible** des conteneurs.
- Interaction **précise** avec les éléments.
- Utilisation avec les **algorithmes génériques**.
- **Exemple de parcours** :
```cpp
vector<int>::iterator it;
for (it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
```
### 🗃️ **Types de conteneurs STL**
| Type | Exemples | Utilisation |
|---------------|-----------------------------------|---------------------------------|
| **Séquence** | `vector`, `list` | Stockage dynamique, accès index.|
| **Associatifs**| `set`, `map` | Clés uniques, paires clé/valeur.|
| **Adaptateurs**| `stack`, `queue`, `priority_queue`| Structures LIFO, FIFO, triée. |
### ⚙️ **Bibliothèque `<algorithm>`**
- **Fonctions clés** : `sort`, `find`, `count`, `copy`, etc.
- **Utilisation** : Avec les **itérateurs**.
```cpp
sort(v.begin(), v.end());
```
---
## 🧩 **Ancrage Mémoriel**
- **Héritage** = **Réutilisation** + **Spécialisation** (ex: `Animal` → `Chien`).
- **`protected`** = Accès **limité** à la classe fille.
- **Polymorphisme** = Méthode `virtual` + **type réel** de l’objet.
- **Itérateurs** = **Flexibilité** pour parcourir les conteneurs.
- **Masquage** = Méthode fille **cache** la méthode mère.{
"questions": [
{
"question": "Quel est le but principal de l'héritage en programmation orientée objet (POO) ?",
"options": [
{
"text": "Permettre la duplication de code entre classes.",
"why": "L'héritage vise à éviter la duplication de code, pas à la favoriser. Il permet la réutilisation et la spécialisation du code existant.",
"correct": false
},
{
"text": "Réutiliser du code, spécialiser des classes et établir une hiérarchie logique.",
"why": "C'est le but principal de l'héritage : réutiliser le code d'une classe mère, spécialiser les classes filles et organiser les classes dans une hiérarchie logique.",
"correct": true
},
{
"text": "Rendre tous les membres d'une classe mère accessibles à l'extérieur.",
"why": "L'héritage ne modifie pas les règles d'accès des membres d'une classe mère. Les membres `private` restent inaccessibles, même pour les classes filles.",
"correct": false
},
{
"text": "Créer des instances indépendantes sans lien entre elles.",
"why": "L'héritage crée un lien hiérarchique entre les classes (mère et fille), ce qui est l'opposé de la création d'instances indépendantes.",
"correct": false
}
]
},
{
"question": "Dans le contexte de l'héritage, que signifie un membre `protected` dans une classe mère ?",
"options": [
{
"text": "Il est accessible uniquement par la classe mère et inaccessible partout ailleurs.",
"why": "Un membre `protected` est accessible par la classe mère **et** par les classes filles, mais pas de l'extérieur.",
"correct": false
},
{
"text": "Il est accessible par la classe mère, les classes filles, et de l'extérieur.",
"why": "Un membre `protected` n'est **pas** accessible de l'extérieur. Seules la classe mère et les classes filles y ont accès.",
"correct": false
},
{
"text": "Il est accessible par la classe mère et les classes filles, mais pas de l'extérieur.",
"why": "C'est la définition exacte d'un membre `protected` dans le contexte de l'héritage.",
"correct": true
},
{
"text": "Il est automatiquement converti en `public` dans les classes filles.",
"why": "Le niveau de protection d'un membre `protected` dépend du type d'héritage (`public`, `protected` ou `private`), mais il n'est pas automatiquement converti en `public`.",
"correct": false
}
]
},
{
"question": "Quel est l'ordre d'appel des constructeurs lors de la création d'un objet d'une classe fille ?",
"options": [
{
"text": "Le constructeur de la classe fille est appelé en premier, suivi de celui de la classe mère.",
"why": "L'ordre est inverse : le constructeur de la classe mère est appelé en premier, suivi de celui de la classe fille. Cela permet d'initialiser les attributs hérités avant ceux de la classe fille.",
"correct": false
},
{
"text": "Le constructeur de la classe mère est appelé en premier, suivi de celui de la classe fille.",
"why": "C'est l'ordre correct. Les constructeurs sont appelés de manière hiérarchique, de la classe la plus générale à la plus spécifique.",
"correct": true
},
{
"text": "Les constructeurs de la classe mère et de la classe fille sont appelés en parallèle.",
"why": "Les constructeurs ne sont pas appelés en parallèle. Ils sont exécutés séquentiellement, en commençant par la classe mère.",
"correct": false
},
{
"text": "Seul le constructeur de la classe fille est appelé, car il hérite de tous les attributs de la classe mère.",
"why": "Le constructeur de la classe mère est toujours appelé pour initialiser les attributs hérités, même si la classe fille définit ses propres attributs.",
"correct": false
}
]
},
{
"question": "Que se passe-t-il lorsqu'une méthode de la classe fille a le même nom qu'une méthode de la classe mère ?",
"options": [
{
"text": "La méthode de la classe mère est appelée par défaut, sauf si elle est déclarée `virtual`.",
"why": "C'est l'inverse : la méthode de la classe fille masque celle de la classe mère, sauf si la méthode mère est déclarée `virtual` et qu'on utilise un pointeur ou une référence de type mère.",
"correct": false
},
{
"text": "La méthode de la classe fille masque celle de la classe mère, sauf si la méthode mère est déclarée `virtual`.",
"why": "C'est le phénomène de masquage : la méthode de la classe fille prend le pas sur celle de la classe mère, sauf dans le cas du polymorphisme avec des méthodes `virtual`.",
"correct": true
},
{
"text": "Les deux méthodes sont fusionnées en une seule méthode qui combine leurs comportements.",
"why": "Il n'y a pas de fusion automatique des méthodes. La méthode de la classe fille masque celle de la classe mère, sauf si on appelle explicitement la méthode mère avec `ClasseMere::methode()`.",
"correct": false
},
{
"text": "La méthode de la classe mère est supprimée et remplacée par celle de la classe fille.",
"why": "La méthode de la classe mère n'est pas supprimée. Elle est simplement masquée et peut être appelée explicitement si nécessaire.",
"correct": false
}
]
},
{
"question": "Pourquoi déclare-t-on une méthode comme `virtual` dans une classe mère ?",
"options": [
{
"text": "Pour permettre à la méthode d'être appelée même si elle est masquée par une méthode de la classe fille.",
"why": "Une méthode `virtual` permet le polymorphisme : l'appel de la méthode dépend du type réel de l'objet (et non du type statique du pointeur ou de la référence). Cela ne concerne pas directement le masquage.",
"correct": false
},
{
"text": "Pour que l'appel de la méthode dépende du type réel de l'objet pointé, et non du type statique du pointeur.",
"why": "C'est la raison principale de l'utilisation de `virtual`. Cela permet le polymorphisme, où le comportement de la méthode s'adapte au type réel de l'objet.",
"correct": true
},
{
"text": "Pour rendre la méthode accessible depuis l'extérieur de la classe.",
"why": "Le mot-clé `virtual` n'a aucun impact sur l'accessibilité d'une méthode. Il est utilisé pour activer le polymorphisme.",
"correct": false
},
{
"text": "Pour empêcher la méthode d'être redéfinie dans les classes filles.",
"why": "C'est l'inverse : `virtual` permet (et encourage) la redéfinition de la méthode dans les classes filles pour activer le polymorphisme.",
"correct": false
}
]
},
{
"question": "Quel est l'avantage principal des itérateurs en C++ ?",
"options": [
{
"text": "Ils permettent de parcourir uniquement les conteneurs de type `vector`.",
"why": "Les itérateurs sont conçus pour être génériques et fonctionnent avec **tous** les conteneurs de la STL, pas seulement les `vector`.",
"correct": false
},
{
"text": "Ils offrent une flexibilité pour parcourir tous les conteneurs de la STL et interagir précisément avec leurs éléments.",
"why": "C'est l'avantage principal des itérateurs : ils permettent de parcourir et manipuler les éléments de n'importe quel conteneur STL de manière uniforme et flexible.",
"correct": true
},
{
"text": "Ils remplacent complètement les boucles `for` traditionnelles.",
"why": "Les itérateurs ne remplacent pas les boucles `for`, mais ils offrent une alternative plus flexible et générique pour parcourir les conteneurs.",
"correct": false
},
{
"text": "Ils sont utilisés uniquement pour trier les éléments d'un conteneur.",
"why": "Les itérateurs ne sont pas limités au tri. Ils permettent de parcourir, accéder et manipuler les éléments d'un conteneur, et sont utilisés par de nombreuses fonctions de la bibliothèque `<algorithm>`.",
"correct": false
}
]
},
{
"question": "Quelle est la différence entre un conteneur séquentiel et un conteneur associatif dans la STL ?",
"options": [
{
"text": "Les conteneurs séquentiels stockent des éléments dans un ordre défini par l'utilisateur, tandis que les conteneurs associatifs stockent des éléments sans ordre particulier.",
"why": "Les conteneurs associatifs stockent les éléments selon un ordre défini par une clé (par exemple, triés par ordre croissant pour un `set`), pas sans ordre particulier.",
"correct": false
},
{
"text": "Les conteneurs séquentiels stockent des éléments dans un ordre défini par l'utilisateur, tandis que les conteneurs associatifs stockent des éléments selon une clé (ex: triés ou par paires clé/valeur).",
"why": "C'est la différence fondamentale : les conteneurs séquentiels (`vector`, `list`) maintiennent l'ordre d'insertion, tandis que les conteneurs associatifs (`set`, `map`) organisent les éléments selon une clé.",
"correct": true
},
{
"text": "Les conteneurs séquentiels sont plus rapides pour les recherches que les conteneurs associatifs.",
"why": "C'est l'inverse : les conteneurs associatifs (`set`, `map`) sont optimisés pour les recherches rapides, tandis que les conteneurs séquentiels (`vector`, `list`) ne le sont pas.",
"correct": false
},
{
"text": "Les conteneurs séquentiels ne peuvent pas être modifiés après leur création, contrairement aux conteneurs associatifs.",
"why": "Les deux types de conteneurs peuvent être modifiés après leur création (ajout, suppression d'éléments).",
"correct": false
}
]
},
{
"question": "Dans quel cas utiliserait-on un héritage de type `private` ?",
"options": [
{
"text": "Pour rendre tous les membres `public` et `protected` de la classe mère accessibles comme `public` dans la classe fille.",
"why": "Un héritage `private` convertit tous les membres `public` et `protected` de la classe mère en `private` dans la classe fille, pas en `public`.",
"correct": false
},
{
"text": "Pour masquer complètement l'héritage et rendre tous les membres hérités `private` dans la classe fille, même s'ils étaient `public` ou `protected` dans la classe mère.",
"why": "C'est le rôle de l'héritage `private` : il convertit tous les membres hérités (`public` et `protected`) en `private` dans la classe fille, masquant ainsi l'héritage de l'extérieur.",
"correct": true
},
{
"text": "Pour permettre à la classe fille d'accéder aux membres `private` de la classe mère.",
"why": "Aucun type d'héritage ne permet d'accéder aux membres `private` d'une classe mère. Seuls les membres `protected` et `public` sont accessibles, selon le type d'héritage.",
"correct": false
},
{
"text": "Pour créer une relation « est-un » entre la classe mère et la classe fille, visible de l'extérieur.",
"why": "L'héritage `private` masque la relation « est-un » de l'extérieur. Seuls les héritages `public` et `protected` préservent cette relation de manière visible.",
"correct": false
}
]
},
{
"question": "Quelle est la bonne pratique lorsqu'on redéfinit une méthode dans une classe fille pour personnaliser son comportement ?",
"options": [
{
"text": "Ignorer complètement la méthode de la classe mère et écrire une nouvelle implémentation.",
"why": "Ignorer la méthode de la classe mère peut entraîner de la duplication de code et rendre le code moins maintenable. Il est préférable d'appeler la méthode mère avant d'ajouter des spécificités.",
"correct": false
},
{
"text": "Appeler la méthode de la classe mère (`ClasseMere::methode()`) puis ajouter les spécificités de la classe fille pour éviter la duplication de code.",
"why": "C'est la bonne pratique : appeler la méthode mère permet de réutiliser son comportement, puis d'ajouter les spécificités de la classe fille.",
"correct": true
},
{
"text": "Déclarer la méthode de la classe mère comme `private` pour forcer la classe fille à redéfinir complètement la méthode.",
"why": "Déclarer la méthode mère comme `private` l'empêcherait d'être accessible dans la classe fille, ce qui n'est pas souhaitable pour la réutilisation de code.",
"correct": false
},
{
"text": "Utiliser uniquement des méthodes `virtual` pour éviter d'avoir à appeler explicitement la méthode mère.",
"why": "Les méthodes `virtual` permettent le polymorphisme, mais ne dispensent pas d'appeler explicitement la méthode mère si on souhaite réutiliser son comportement dans la classe fille.",
"correct": false
}
]
},
{
"question": "Quel est l'intérêt d'utiliser la bibliothèque `<algorithm>` avec des itérateurs ?",
"options": [
{
"text": "Elle permet de créer des conteneurs personnalisés qui ne sont pas disponibles dans la STL.",
"why": "La bibliothèque `<algorithm>` ne crée pas de conteneurs. Elle fournit des fonctions génériques pour manipuler les éléments des conteneurs existants (tri, recherche, etc.).",
"correct": false
},
{
"text": "Elle offre des fonctions génériques (comme `sort`, `find`, `count`) qui peuvent être utilisées avec n'importe quel conteneur via des itérateurs.",
"why": "C'est l'intérêt principal de `<algorithm>` : elle fournit des fonctions génériques qui fonctionnent avec tous les conteneurs de la STL grâce aux itérateurs.",
"correct": true
},
{
"text": "Elle remplace les itérateurs par des boucles `for` traditionnelles pour simplifier le code.",
"why": "La bibliothèque `<algorithm>` utilise les itérateurs pour fonctionner, elle ne les remplace pas par des boucles `for`.",
"correct": false
},
{
"text": "Elle permet de convertir automatiquement les conteneurs séquentiels en conteneurs associatifs.",
"why": "La bibliothèque `<algorithm>` ne convertit pas les conteneurs. Elle fournit des outils pour manipuler leurs éléments.",
"correct": false
}
]
}
]
}# Programmation Orientée Objet : Héritage, Polymorphisme et Conteneurs STL
## Chapitre 1 : L'Héritage en POO
### Objectifs d'Apprentissage 🎯
- Comprendre le mécanisme d'héritage et ses objectifs en POO.
- Maîtriser la terminologie associée (classe mère/fille, base/dérivée).
- Connaître les règles d'accès aux membres hérités.
- Distinguer les différents types d'héritage (`public`, `protected`, `private`).
### 1.1 Définition et Objectifs
L'héritage est un **mécanisme fondamental de la POO** permettant de créer une nouvelle classe (dite *fille* ou *dérivée*) à partir d'une classe existante (dite *mère* ou *base*). Ce mécanisme permet :
- **La réutilisation du code** : La classe fille hérite des attributs et méthodes de la classe mère.
- **La spécialisation** : La classe fille peut ajouter de nouveaux membres ou redéfinir des méthodes existantes.
- **La hiérarchie logique** : Organisation des classes en une structure arborescente reflétant des relations de type "est-un" (ex: un `Chien` *est un* `Animal`).
```mermaid
classDiagram
Animal <|-- Chien
class Animal {
+manger()
+dormir()
}
class Chien {
+aboyer()
}
```
### 1.2 Règles d'Accès aux Membres Hérités
Les membres d'une classe mère peuvent avoir trois niveaux de visibilité, qui déterminent leur accessibilité dans la classe fille :
| Niveau de Visibilité | Accessible dans la Classe Fille ? | Accessible depuis l'Extérieur ? |
|----------------------|-----------------------------------|-----------------------------------|
| `private` | ❌ Non | ❌ Non |
| `protected` | ✅ Oui | ❌ Non |
| `public` | ✅ Oui | ✅ Oui |
> **Notes de l'IA 🧑🏫 :**
> - Les membres `private` d'une classe mère sont **strictement réservés** à cette classe. Même une classe fille ne peut y accéder directement. Pour permettre un accès contrôlé, utilisez `protected`.
> - Le mot-clé `protected` est souvent utilisé pour les attributs ou méthodes qui doivent être accessibles aux classes dérivées, mais pas au reste du programme.
### 1.3 Types d'Héritage
Le type d'héritage détermine comment les niveaux de visibilité des membres de la classe mère sont modifiés dans la classe fille :
| Type d'Héritage | Effet sur les Membres de la Classe Mère |
|-----------------|------------------------------------------|
| `public` | Les membres `public`/`protected` restent inchangés. |
| `protected` | Les membres `public`/`protected` deviennent `protected`. |
| `private` | Les membres `public`/`protected` deviennent `private`. |
**Exemple en C++ :**
```cpp
class Animal {
public:
void manger();
protected:
void dormir();
private:
int age;
};
// Héritage public
class Chien : public Animal {
// manger() reste public
// dormir() reste protected
// age reste inaccessible
};
// Héritage protected
class Chat : protected Animal {
// manger() devient protected
// dormir() reste protected
// age reste inaccessible
};
// Héritage private
class Oiseau : private Animal {
// manger() devient private
// dormir() devient private
// age reste inaccessible
};
```
### Points Clés à Retenir 📌
- L'héritage permet de **réutiliser et spécialiser** le code existant.
- Une classe fille **hérite de tous les membres** de la classe mère, mais leur accessibilité dépend de leur niveau de visibilité initial et du type d'héritage.
- Les membres `private` d'une classe mère sont **toujours inaccessibles** à la classe fille.
- L'héritage `public` est le plus couramment utilisé, car il préserve la relation "est-un".
---
## Chapitre 2 : Héritage et Constructeurs
### Objectifs d'Apprentissage 🎯
- Comprendre l'ordre d'appel des constructeurs dans une hiérarchie d'héritage.
- Savoir initialiser les attributs hérités via le constructeur de la classe mère.
- Maîtriser l'utilisation de la liste d'initialisation pour appeler un constructeur spécifique de la classe mère.
### 2.1 Appel en Cascade des Constructeurs
Lors de la création d'un objet d'une classe dérivée, les constructeurs sont appelés dans un **ordre hiérarchique** :
1. **Constructeur de la classe mère** (de la plus générale à la plus spécifique).
2. **Constructeur de la classe fille**.
**Exemple :**
```cpp
class Animal {
public:
Animal() { std::cout << "Constructeur Animal\n"; }
};
class Chien : public Animal {
public:
Chien() { std::cout << "Constructeur Chien\n"; }
};
int main() {
Chien monChien; // Affiche : "Constructeur Animal" puis "Constructeur Chien"
return 0;
}
```
### 2.2 Initialisation des Attributs Hérités
Les attributs hérités de la classe mère sont **initialisés par le constructeur de la classe mère**. Il est donc crucial de bien définir les constructeurs de la classe mère pour garantir une initialisation correcte.
> **Notes de l'IA 🧑🏫 :**
> - Si la classe mère n'a pas de constructeur par défaut (sans paramètres), vous **devez** appeler explicitement un constructeur de la classe mère dans la liste d'initialisation de la classe fille.
> - Oublier d'appeler le constructeur de la classe mère lorsque celui-ci n'a pas de constructeur par défaut entraîne une **erreur de compilation**.
### 2.3 Choix du Constructeur de la Classe Mère
Pour appeler un constructeur spécifique de la classe mère, utilisez la **liste d'initialisation** dans le constructeur de la classe fille.
**Exemple :**
```cpp
class Personne {
public:
Personne(std::string nom, int age) : nom_(nom), age_(age) {}
private:
std::string nom_;
int age_;
};
class Etudiant : public Personne {
public:
Etudiant(std::string nom, int age, int numeroEtudiant)
: Personne(nom, age), numeroEtudiant_(numeroEtudiant) {}
private:
int numeroEtudiant_;
};
```
### Points Clés à Retenir 📌
- Les constructeurs sont appelés dans un **ordre hiérarchique**, de la classe mère à la classe fille.
- Les attributs hérités sont initialisés par le **constructeur de la classe mère**.
- Utilisez la **liste d'initialisation** pour appeler un constructeur spécifique de la classe mère.
- Si la classe mère n'a pas de constructeur par défaut, vous **devez** appeler explicitement un constructeur de la classe mère.
---
## Chapitre 3 : Le Masquage de Méthodes
### Objectifs d'Apprentissage 🎯
- Comprendre le mécanisme de masquage des méthodes en POO.
- Savoir quand et pourquoi masquer une méthode de la classe mère.
- Connaître l'impact du type statique sur l'appel des méthodes masquées.
- Appliquer les bonnes pratiques pour éviter la duplication de code.
### 3.1 Définition et Mécanisme
Le **masquage** (ou *hiding*) se produit lorsqu'une classe fille définit une méthode portant le **même nom** qu'une méthode de la classe mère. Dans ce cas :
- La méthode de la classe mère est **masquée** pour les objets de la classe fille.
- La méthode appelée dépend du **type statique** de la variable (et non du type réel de l'objet).
**Exemple :**
```cpp
class Animal {
public:
void afficher() { std::cout << "Animal\n"; }
};
class Chien : public Animal {
public:
void afficher() { std::cout << "Chien\n"; }
};
int main() {
Chien monChien;
Animal* ptrAnimal = &monChien;
monChien.afficher(); // Affiche "Chien" (type statique : Chien)
ptrAnimal->afficher(); // Affiche "Animal" (type statique : Animal*)
return 0;
}
```
### 3.2 Raison d'Être du Masquage
Le masquage permet de **personnaliser le comportement** d'une classe fille tout en conservant la structure héritée de la classe mère. Cela est particulièrement utile pour :
- **Adapter une méthode** aux besoins spécifiques de la classe fille.
- **Ajouter des fonctionnalités** tout en réutilisant le code de la classe mère.
### 3.3 Bonnes Pratiques
Pour éviter la duplication de code et garantir une bonne maintenabilité, il est recommandé de :
1. **Appeler la méthode de la classe mère** depuis la méthode masquée de la classe fille.
2. **Ajouter les spécificités** de la classe fille après cet appel.
**Exemple de Bonne Pratique :**
```cpp
class Animal {
public:
void afficher() { std::cout << "Je suis un animal. "; }
};
class Chien : public Animal {
public:
void afficher() {
Animal::afficher(); // Appel de la méthode de la classe mère
std::cout << "Plus précisément, je suis un chien.\n";
}
};
```
> **Notes de l'IA 🧑🏫 :**
> - Le masquage **n'est pas du polymorphisme**. Pour activer le polymorphisme, il faut utiliser des méthodes `virtual` (voir Chapitre 4).
> - Le masquage peut entraîner des comportements inattendus si le type statique de la variable ne correspond pas au type réel de l'objet. Soyez vigilant !
### Points Clés à Retenir 📌
- Le masquage se produit lorsqu'une classe fille définit une méthode de **même nom** qu'une méthode de la classe mère.
- La méthode appelée dépend du **type statique** de la variable, pas du type réel de l'objet.
- Pour personnaliser une méthode tout en réutilisant le code de la classe mère, **appelez d'abord la méthode de la classe mère** (`ClasseMere::methode()`).
- Le masquage **ne remplace pas le polymorphisme**, qui nécessite des méthodes `virtual`.
---
## Chapitre 4 : Le Polymorphisme
### Objectifs d'Apprentissage 🎯
- Comprendre la problématique résolue par le polymorphisme.
- Savoir déclarer et utiliser des méthodes `virtual`.
- Maîtriser l'impact du polymorphisme sur l'appel des méthodes.
- Connaître les bonnes pratiques pour implémenter le polymorphisme.
### 4.1 Problématique du Polymorphisme
Le polymorphisme permet de **stocker et manipuler des objets de types différents dans un même conteneur**, tout en garantissant que la **bonne méthode** est appelée en fonction du **type réel** de l'objet.
**Exemple de Problématique :**
```cpp
class Personne {
public:
void afficher() { std::cout << "Personne\n"; }
};
class Etudiant : public Personne {
public:
void afficher() { std::cout << "Etudiant\n"; }
};
int main() {
std::vector<Personne*> personnes;
personnes.push_back(new Personne());
personnes.push_back(new Etudiant());
for (Personne* p : personnes) {
p->afficher(); // Affiche toujours "Personne" (pas de polymorphisme)
}
return 0;
}
```
Dans cet exemple, bien que le deuxième élément du vecteur soit un `Etudiant`, la méthode `afficher()` de `Personne` est appelée, car la méthode n'est pas `virtual`.
### 4.2 Solution : Les Méthodes `virtual`
Pour activer le polymorphisme, déclarez la méthode dans la classe mère avec le mot-clé `virtual`. Ainsi, l'appel de la méthode dépendra du **type réel** de l'objet, et non du type statique du pointeur ou de la référence.
**Exemple avec Polymorphisme :**
```cpp
class Personne {
public:
virtual void afficher() { std::cout << "Personne\n"; }
};
class Etudiant : public Personne {
public:
void afficher() override { std::cout << "Etudiant\n"; }
};
int main() {
std::vector<Personne*> personnes;
personnes.push_back(new Personne());
personnes.push_back(new Etudiant());
for (Personne* p : personnes) {
p->afficher(); // Affiche "Personne" puis "Etudiant" (polymorphisme activé)
}
return 0;
}
```
> **Notes de l'IA 🧑🏫 :**
> - Le mot-clé `override` (C++11 et ultérieur) permet de **vérifier à la compilation** que la méthode redéfinie dans la classe fille est bien `virtual` dans la classe mère. Cela évite les erreurs de typographie ou de signature.
> - Une méthode `virtual` dans une classe mère **reste `virtual`** dans toutes les classes filles, même si le mot-clé `virtual` n'est pas répété.
> - Pour activer pleinement le polymorphisme, utilisez toujours des **pointeurs ou des références** (ex: `Personne*`, `Personne&`). Les objets eux-mêmes ne bénéficient pas du polymorphisme.
### 4.3 Destructeurs `virtual`
Si une classe est conçue pour être héritée, son **destructeur doit être déclaré `virtual`**. Cela garantit que le destructeur de la classe fille est appelé lorsque l'objet est détruit via un pointeur de la classe mère.
**Exemple :**
```cpp
class Personne {
public:
virtual ~Personne() {}
};
class Etudiant : public Personne {
public:
~Etudiant() override { std::cout << "Destruction Etudiant\n"; }
};
int main() {
Personne* p = new Etudiant();
delete p; // Appelle ~Etudiant() puis ~Personne() (grâce au destructeur virtual)
return 0;
}
```
> **Notes de l'IA 🧑🏫 :**
> - Oublier de déclarer un destructeur `virtual` dans une classe mère peut entraîner des **fuites mémoire** ou des comportements indéfinis, car seul le destructeur de la classe mère sera appelé.
> - Si une classe n'est **pas conçue pour être héritée**, son destructeur n'a pas besoin d'être `virtual`. Cependant, il est souvent recommandé de le déclarer `virtual` par précaution.
### Points Clés à Retenir 📌
- Le polymorphisme permet d'appeler la **bonne méthode** en fonction du **type réel** de l'objet (et non du type statique).
- Pour activer le polymorphisme, déclarez la méthode dans la classe mère avec le mot-clé `virtual`.
- Utilisez toujours des **pointeurs ou des références** pour bénéficier du polymorphisme.
- **Déclarez le destructeur `virtual`** dans une classe mère pour garantir une destruction correcte des objets dérivés.
- Le mot-clé `override` permet de vérifier que la méthode redéfinie est bien `virtual` dans la classe mère.
---
## Chapitre 5 : Conteneurs et Itérateurs en C++
### Objectifs d'Apprentissage 🎯
- Comprendre l'utilité des itérateurs et leur rôle dans la STL.
- Savoir parcourir un conteneur à l'aide d'itérateurs.
- Connaître les principaux conteneurs de la STL et leurs caractéristiques.
- Utiliser les algorithmes génériques de la bibliothèque `<algorithm>` avec des itérateurs.
### 5.1 Les Itérateurs
Les itérateurs sont des **objets généralisés** permettant de parcourir et d'interagir avec les éléments d'un conteneur, indépendamment de son type. Ils offrent plusieurs avantages :
- **Flexibilité** : Compatibles avec tous les conteneurs de la STL.
- **Précision** : Permettent un accès fin aux éléments (lecture, modification, insertion, suppression).
- **Généricité** : Utilisables avec les algorithmes de la bibliothèque `<algorithm>`.
**Exemple de Parcours avec Itérateur :**
```cpp
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {10, 20, 30, 40};
// Déclaration d'un itérateur
std::vector<int>::iterator it;
// Parcours du vecteur
for (it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " "; // Affiche : 10 20 30 40
}
return 0;
}
```
> **Notes de l'IA 🧑🏫 :**
> - Les itérateurs se comportent comme des **pointeurs intelligents**. L'opérateur `*` permet d'accéder à l'élément pointé, et l'opérateur `++` permet de passer à l'élément suivant.
> - `v.begin()` retourne un itérateur pointant sur le **premier élément** du conteneur.
> - `v.end()` retourne un itérateur pointant sur la **position juste après le dernier élément** (et non sur le dernier élément).
> - Pour les conteneurs constants, utilisez `const_iterator` pour éviter les modifications accidentelles.
### 5.2 Les Conteneurs de la STL
La STL propose plusieurs types de conteneurs, chacun adapté à des besoins spécifiques :
#### Conteneurs Séquentiels
| Conteneur | Description | Accès | Insertion/Suppression |
|------------|-----------------------------------------------------------------------------|-------|-----------------------|
| `vector` | Tableau dynamique. Taille modifiable, accès aléatoire rapide. | O(1) | O(n) (sauf en fin) |
| `list` | Liste doublement chaînée. Insertion/suppression rapide en milieu de liste. | O(n) | O(1) |
| `deque` | File à double extrémité. Accès aléatoire et insertion/suppression rapides aux deux extrémités. | O(1) | O(1) (aux extrémités) |
#### Conteneurs Associatifs
| Conteneur | Description | Accès |
|------------|-----------------------------------------------------------------------------|-------|
| `set` | Collection d'éléments uniques, triés automatiquement. | O(log n) |
| `map` | Collection de paires clé/valeur, triées par clé. | O(log n) |
| `unordered_set` | Collection d'éléments uniques, non triés (basé sur une table de hachage). | O(1) en moyenne |
| `unordered_map` | Collection de paires clé/valeur, non triées (basé sur une table de hachage). | O(1) en moyenne |
#### Adaptateurs de Conteneurs
| Adaptateur | Description | Conteneur Sous-Jacent |
|------------|-----------------------------------------------------------------------------|-----------------------|
| `stack` | Pile (LIFO : Last In, First Out). | `deque` (par défaut) |
| `queue` | File (FIFO : First In, First Out). | `deque` (par défaut) |
| `priority_queue` | File de priorité. Les éléments sont triés par ordre de priorité. | `vector` (par défaut) |
### 5.3 La Bibliothèque `<algorithm>`
La bibliothèque `<algorithm>` fournit des **algorithmes génériques** qui opèrent sur des plages d'éléments définies par des itérateurs. Voici quelques fonctions couramment utilisées :
| Fonction | Description | Exemple d'Utilisation |
|---------------|-----------------------------------------------------------------------------|-------------------------------------------|
| `sort` | Trie les éléments d'une plage. | `sort(v.begin(), v.end());` |
| `find` | Recherche un élément dans une plage. | `auto it = find(v.begin(), v.end(), 42);` |
| `count` | Compte le nombre d'occurrences d'une valeur dans une plage. | `int n = count(v.begin(), v.end(), 42);` |
| `copy` | Copie les éléments d'une plage vers une autre. | `copy(v1.begin(), v1.end(), v2.begin());` |
| `reverse` | Inverse l'ordre des éléments dans une plage. | `reverse(v.begin(), v.end());` |
**Exemple d'Utilisation :**
```cpp
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {40, 10, 30, 20};
// Tri du vecteur
sort(v.begin(), v.end());
// Recherche d'un élément
auto it = find(v.begin(), v.end(), 30);
if (it != v.end()) {
std::cout << "Element trouvé : " << *it << "\n";
}
return 0;
}
```
### Points Clés à Retenir 📌
- Les **itérateurs** permettent de parcourir et manipuler les éléments d'un conteneur de manière générique.
- La STL propose une **variété de conteneurs** adaptés à différents besoins (accès rapide, insertion/suppression efficace, tri automatique, etc.).
- Les **algorithmes génériques** de la bibliothèque `<algorithm>` fonctionnent avec tous les conteneurs via les itérateurs.
- Choisissez le conteneur en fonction des **opérations les plus fréquentes** dans votre programme (ex: `vector` pour un accès aléatoire fréquent, `list` pour des insertions/suppressions fréquentes en milieu de conteneur).
- Les **adaptateurs de conteneurs** (`stack`, `queue`, `priority_queue`) fournissent des interfaces spécialisées pour des structures de données courantes.