Préparation au final
Stratégie : STL, foncteurs, lambdas, exceptions, PythonQuiz de révision (33 questions)5 exercices de synthèseChronomètreListe de vérification
Le final reprend tout ce que l'intra évaluait (classes, copie, opérateurs, héritage, polymorphisme, templates) et y ajoute les modules 8 à 10. Les questions longues combinent typiquement une hiérarchie polymorphe, un conteneur de la STL manipulé par des algorithmes et des lambdas, des exceptions pour les cas d'erreur, et parfois une variable de classe. La page Intra reste valable pour la première moitié de la matière.
1. Préparer sa clé USB et ses feuilles
- Le site complet (il fonctionne hors ligne) et l'aide-mémoire imprimé, sections 8 à 10 comprises : tableau des conteneurs, liste des algorithmes, syntaxe des lambdas, hiérarchie des exceptions, correspondances C++ / Python.
- Les TP4 (STL) et TP5 (Python) corrigés : ce sont vos meilleurs modèles de code pour les algorithmes avec foncteurs et lambdas, et pour la syntaxe Python.
- Le fichier d'exercices du simulateur de hockey (versions héritage, polymorphisme, conteneurs, algorithmes, exceptions) : l'examen puise souvent dans le même genre de scénario (équipes, joueurs, cap salarial).
- Vérifiez sur place que la clé s'ouvre : aucun problème d'accès aux fichiers n'est pris en compte pendant l'examen.
2. Stratégie par type de question
« Réécrivez sans boucle avec les algorithmes de la STL »
Identifiez le motif de la boucle, puis l'algorithme qui le porte. Les paramètres de la boucle deviennent les captures de la lambda.
| La boucle fait... | Algorithme | Squelette |
|---|---|---|
| appliquer une action à chaque élément | for_each | for_each(b, e, [&](const T& x) { ... }); |
| chercher le premier élément qui vérifie une condition | find_if (ou find pour une valeur) | auto it = find_if(b, e, [seuil](const T& x) { return ...; }); if (it != e) ... |
| compter | count_if / count | int n = count_if(b, e, pred); |
| additionner, cumuler | accumulate (<numeric>) | double s = accumulate(b, e, 0.0, [](double t, const T& x) { return t + x.val(); }); |
| tester « tous », « au moins un », « aucun » | all_of, any_of, none_of | bool ok = all_of(b, e, pred); |
| garder les éléments qui vérifient une condition dans un autre conteneur | copy_if + back_inserter | copy_if(b, e, back_inserter(w), pred); |
| retirer ceux qui vérifient une condition | erase + remove_if (ou list::remove_if) | v.erase(remove_if(b, e, pred), e); |
| transformer chaque élément | transform | transform(b, e, dest, f); |
| trouver le plus grand ou le plus petit | max_element / min_element | auto it = max_element(b, e, comp); |
| trier (les trois premiers suffisent) | sort (partial_sort) | sort(b, e, [](const T& a, const T& c) { return a.x() < c.x(); }); |
| afficher les éléments | copy + ostream_iterator, ou for_each | copy(b, e, ostream_iterator<T>(cout, "\n")); (exige operator<<) |
Après avoir écrit la lambda, vérifiez trois choses : chaque variable extérieure utilisée est capturée (par référence si on la modifie, sinon par valeur) ; dans une méthode, les attributs demandent [this] ; un comparateur reçoit deux paramètres par référence constante et retourne un ordre strict (<, jamais <=).
« Écrivez un foncteur qui ... »
Une classe dont operator() fait le travail d'une itération ; les valeurs fixées par l'appelant (seuil, équipe, flux) deviennent des attributs initialisés par le constructeur ; les résultats accumulés deviennent des attributs lus par un accesseur. Si l'énoncé impose « au plus deux foncteurs », un même foncteur peut servir de comparateur (pour sort ou set) et de prédicat selon la surcharge d'operator(). Rappelez-vous que for_each reçoit une copie : récupérez le foncteur retourné pour lire son état.
« Quel conteneur choisir ? Justifiez. »
Répondez avec le critère décisif : accès par indice (vector), insertion et retrait aux extrémités (deque), insertions au milieu sans invalider les itérateurs (list), unicité et tri automatique (set), association clé → valeur triée (map), plusieurs valeurs par clé (multimap), vitesse sans ordre (unordered_map), discipline LIFO / FIFO / priorité (stack, queue, priority_queue). Donnez la complexité de l'opération principale (O(1), O(log n), O(n)).
« Gérez les erreurs avec des exceptions »
- Une classe d'exception par cas d'erreur, dérivée de
std::exception(viaruntime_erroroulogic_error, ou d'une base commune abstraite commeExceptionBaseavec une méthode virtuelle pureget_erreur()), qui porte les données utiles au message (équipe, masse, indice). throwà l'endroit où l'erreur est détectée, avant toute modification de l'objet (pas de mise à jour partielle).tryautour de l'appel,catchpar référence constante, dérivées avant bases,catch (...)en dernier ; reprendre le programme après lecatch(menu réaffiché, valeur de retourfalse...).- Les ressources locales doivent être des objets qui se libèrent seuls (pointeurs intelligents, conteneurs) : pendant le dépilement, un
deleteplacé après l'appel n'est jamais exécuté.
« Donnez l'affichage » : les nouveautés du final
- Exceptions : dès le
throw, le reste du bloctry(et des fonctions traversées) est abandonné ; les objets locaux de chaque fonction quittée sont détruits avant l'exécution ducatch; après lecatch, l'exécution continue normalement. - Lambdas : une capture par valeur est figée à la création de la lambda, pas à son appel ; une capture par référence suit la variable ;
mutableconserve l'état entre les appels. - Foncteurs : passé par valeur à un algorithme, l'original ne change pas.
- Variables de classe : une seule valeur partagée, incrémentée par chaque construction (y compris les copies si le constructeur de copie le fait), décrémentée par chaque destruction.
- Conteneurs : un
setet unemaps'affichent triés par clé, pas dans l'ordre d'insertion ;m[k]dans un affichage crée la clé ;uniquene retire que les doublons consécutifs.
« Écrivez en Python ... »
Indentation de 4 espaces, deux-points en fin de ligne d'en-tête, self en premier paramètre de chaque méthode, __init__ pour le constructeur, super().__init__(...) pour la base, __str__ pour l'affichage, pas de type ni de point-virgule, True / False / None, and / or / not, raise et except X as e, listes [] avec append, dictionnaires {} avec get, len(), range(), sorted(..., key=lambda ...), compréhensions [f(x) for x in v if c(x)]. Relisez la table de correspondance du module 10 avant d'écrire.
3. Quiz de révision (modules 1 à 10)
33 questions, sans ordre de module
Les deux tiers portent sur les modules 8 à 10, le reste sur la matière de l'intra. Lisez les explications : elles donnent la justification attendue.
Quel conteneur garde ses éléments uniques et triés automatiquement ?
vectorlistsetdeque
Que fait v.erase(remove_if(v.begin(), v.end(), pred), v.end()); ?
- Retire le premier élément qui vérifie pred
- Retire vraiment tous les éléments qui vérifient pred : remove_if les compacte en fin, erase coupe la queue
- Vide le vecteur
- Ne compile pas
Dans quel ordre écrire les blocs catch pour out_of_range, logic_error et exception ?
- exception, logic_error, out_of_range
- L'ordre n'a pas d'importance
- logic_error, exception, out_of_range
- out_of_range, logic_error, exception : du plus dérivé au plus général
Que vaut x ? int k = 2; auto f = [k](int n) { return n * k; }; k = 5; int x = f(3);
- 6 : k est copié à la création de la lambda
- 15
- Erreur de compilation
- 0
[&k], x vaudrait 15.Sur une map<string, int> m vide, que fait cout << m["a"]; ?
- Lance out_of_range
- Affiche rien
- Insère la clé « a » avec la valeur 0 et affiche 0 ; m.size() vaut maintenant 1
- Erreur de compilation
Une méthode statique peut :
- Accéder aux attributs non statiques de l'objet courant
- Être appelée sans objet et n'accéder qu'aux membres statiques
- Être virtuelle
- Être const
Classe::methode().Quel est l'affichage ?
- 2 4
- 2 2
- 0 4
- 0 2
Pourquoi sort(l.begin(), l.end()) ne compile-t-il pas pour list<int> l ?
- sort exige des itérateurs à accès aléatoire ; il faut
l.sort() - Les listes sont toujours triées
- sort ne trie que des vector
- Il manque un comparateur
Que se passe-t-il si une exception traverse une fonction qui a fait Robot* r = new Robot; sans pointeur intelligent ?
- r est libéré par le dépilement
- Le programme plante
- Le delete prévu après l'appel n'est jamais exécuté : fuite ; un unique_ptr aurait libéré l'objet
- L'exception est annulée
En Python, après a = [1, 2]; b = a; b.append(3), que vaut a ?
- [1, 2]
- [1, 2, 3] : a et b désignent la même liste
- Erreur
- [3]
b = list(a).Que retourne find_if(v.begin(), v.end(), pred) quand aucun élément ne convient ?
- nullptr
- -1
- Une exception
v.end()
Où définit-on la variable de classe static int Joueur::nombre_ ?
- Une seule fois, hors de la classe, dans Joueur.cpp :
int Joueur::nombre_ = 0; - Dans le constructeur
- Dans main
- Nulle part, la déclaration suffit
inline static int nombre_ = 0; dans la classe.Quel type permet de stocker une lambda dans un attribut de classe ?
autolambda<bool(int)>std::function<bool(int)>void*
Que vaut accumulate(v.begin(), v.end(), 0) pour vector<double> v = {1.5, 1.5} ?
- 3.0
- 2 : la valeur initiale 0 est un int, l'accumulation se fait en int
- 3
- Erreur de compilation
Dans la classe Python class Camion(Vehicule):, comment initialiser la partie Vehicule ?
- Automatiquement
Vehicule::__init__()this.Vehicule(...)super().__init__(immat, masse)dans__init__
Plusieurs joueurs peuvent avoir le même nombre de buts. Pour les classer par buts avec un conteneur associatif, on choisit :
set<int>map<int, Joueur*>multimap<int, Joueur*>unordered_set<int>
Pourquoi un comparateur ne doit-il jamais retourner vrai pour deux éléments égaux (<=) ?
- sort et les set exigent un ordre strict ; sinon comportement indéfini (boucles infinies, éléments perdus)
- Parce que
<=n'est pas surchargeable - Pour la vitesse seulement
- Le compilateur le refuse
<= n'arrive plus à détecter les doublons.Que se passe-t-il si aucun catch ne correspond, jusque dans main ?
- L'exception est ignorée et le programme continue
std::terminate()arrête le programme- main retourne -1
- Le catch le plus proche est pris quand même
catch (...) ou catch (const exception&) de dernier recours dans main.Une fonction générique template <typename T> T maximum(const T&, const T&) est définie dans un fichier .cpp séparé. Symptôme ?
- Tout fonctionne
- Erreur de syntaxe
- Résultat faux à l'exécution
- Erreur d'édition des liens (symbole non résolu) pour chaque type utilisé ailleurs : la définition doit être dans le .h
Après auto it = v.begin(); v.push_back(x); (vector), l'itérateur it :
- Reste valide
- Désigne x
- Peut être invalide si le vecteur a été réalloué
- Devient end()
Quelle lambda compte, dans une méthode de Robot, les composantes plus lourdes que l'attribut seuil_ ?
count_if(b, e, [this](const auto& c) { return c->getMasseKg() > seuil_; })count_if(b, e, [seuil_](const auto& c) { ... })count_if(b, e, [](const auto& c) { return c->getMasseKg() > seuil_; })count_if(b, e, [&seuil_](const auto& c) { ... })
[seuil_] ne compile pas.Quelle affirmation sur catch (const exception& e) est vraie ?
- Il n'attrape que les objets de type exactement exception
- Il attrape exception et toutes ses dérivées (runtime_error, out_of_range, vos classes dérivées) ;
e.what()donne le message de la dérivée grâce au polymorphisme - Il attrape aussi les int lancés
- Il doit être placé en premier
Que vaut [x * x for x in range(5) if x % 2 == 0] ?
- [0, 1, 4, 9, 16]
- [4, 16]
- [0, 4, 16]
- [1, 9]
Que retourne pq.top() pour priority_queue<int> pq contenant 3, 9, 1 ?
- 3 (premier inséré)
- 1
- Le dernier inséré
- 9 : le plus grand d'abord (ordre par défaut avec less)
priority_queue<int, vector<int>, greater<int>>.Quel est l'affichage ?
- +m+a-a-m[x].
- +m+a+b-b-a-m[x].
- +m+a[x]-a-m.
- +m+a-a[x]-m.
Dans map<string, int>, un élément parcouru par for (auto& e : m) est de type :
pair<string, int>pair<const string, int>: la clé est constante, la valeur modifiable (e.second)intstring
e.first est en lecture seule.Pour parcourir un vector en retirant certains éléments, le schéma correct est :
for (auto it = v.begin(); it != v.end(); ++it) if (pred(*it)) v.erase(it);for (size_t i = 0; i < v.size(); ++i) if (pred(v[i])) v.erase(v.begin() + i);for (auto it = v.begin(); it != v.end(); ) { if (pred(*it)) it = v.erase(it); else ++it; }for (int x : v) if (pred(x)) v.erase(x);
Qu'est-ce qu'une classe d'exception bien conçue pour « cap salarial dépassé » ?
intlancé avec un code- Une
stringlancée - Une classe sans lien avec exception
- Une classe dérivée de std::exception (ou d'une base commune du programme) qui mémorise la masse et l'équipe et construit le message dans
what()ouget_erreur()
En Python, l'équivalent de count_if(v.begin(), v.end(), [](int n) { return n > 5; }) est :
len([n for n in v if n > 5])ousum(1 for n in v if n > 5)v.count_if(lambda n: n > 5)count(v, 5)v.count(> 5)
Que fait copy(v.begin(), v.end(), ostream_iterator<Joueur>(cout, "\n")) ?
- Copie v dans un fichier
- Affiche chaque joueur suivi d'un saut de ligne, en appelant operator<< pour Joueur
- Ne compile jamais
- Trie puis affiche
Une lambda déclarée mutable :
- Peut modifier les variables capturées par référence (déjà possible sans mutable)
- Est plus rapide
- Peut modifier ses captures par valeur, qui conservent leur état entre deux appels de la même lambda
- Capture tout par référence
auto gen = [n = 0]() mutable { return ++n; }; donne 1, 2, 3...Combien d'objets Composante existent après ce code, selon le compteur statique ?
- 0
- 2
- 3
- 1 : le moteur survit grâce à c ; le capteur a été détruit par pop_back
Quelle est la différence entre unique de la STL et la suppression des doublons ?
- unique ne retire que les doublons consécutifs et ne change pas la taille : trier d'abord, puis erase
- unique retire tous les doublons du conteneur
- unique trie le conteneur
- unique ne fonctionne qu'avec set
sort(b, e); v.erase(unique(b, e), e);4. Exercices de synthèse (format examen)
Chaque exercice combine la matière de l'intra et celle du final. Écrivez le code en entier avant de regarder la solution.
Bibliothèque : hiérarchie, algorithmes, map, exceptions, static
Un Document (abstrait) a un titre, une année et un état disponible / emprunté ; il connaît son type() (chaîne) et ses fraisRetard(jours), tous deux virtuels purs. Un Livre ajoute un auteur (frais : 0,25 $ par jour) ; une Revue ajoute un numéro (frais : 0,10 $ par jour, plafonnés à 5 $). La classe Document compte le nombre de documents existants dans une variable de classe. emprunter() lance DocumentIndisponible (dérivée de runtime_error, message contenant le titre) si le document est déjà emprunté.
Une Bibliotheque possède ses documents dans un vector<shared_ptr<Document>>. Écrivez, sans boucle explicite : disponibles() (vecteur des documents disponibles), trierParTitre(), nombreDe(const string& type), parType() qui retourne une map<string, vector<shared_ptr<Document>>>, bool emprunter(const string& titre) (lance invalid_argument si le titre est inconnu, retourne false si le document est indisponible, true sinon), fraisTotaux(int jours) et plusRecent().
Solution
Points de barème habituels : classe abstraite avec destructeur virtuel ; override ; exception dérivée de la hiérarchie standard, lancée avant modification ; captures correctes (&type, jours, &r) ; accumulate avec 0.0 ; test de end() ; variable de classe définie hors de la classe.
Match : interface, foncteur validateur, exceptions, index statique
MembreEquipe est une interface (getSalaire(), getEquipe(), getNumero(), afficher(ostream&)). Joueur (nom, numéro, équipe, salaire, buts) et Entraineur (nom, équipe, salaire ; numéro 0) l'implémentent ; chaque Joueur reçoit à sa construction un index unique croissant à partir de 0, mémorisé dans une variable de classe. Match conserve ses membres dans une list<shared_ptr<MembreEquipe>>.
Écrivez : la hiérarchie d'exceptions ExceptionBase (abstraite, get_erreur()), ExceptionNombreJoueur et ExceptionMasseSalariale ; le foncteur FoncteurValider (construit avec l'équipe) qui, appliqué à la liste, lance ExceptionNombreJoueur si l'équipe n'a pas exactement 3 joueurs et 1 entraîneur, et ExceptionMasseSalariale si sa masse salariale dépasse 10 000 000 ; puis les méthodes de Match sans boucle explicite : valider(), masseSalariale(bool equipe), afficherEquipe(bool equipe) (membres de l'équipe par numéro croissant) et afficherTroisEtoiles() (les trois joueurs ayant le plus de buts). Montrez enfin le code du menu qui appelle valider() et affiche le message d'erreur.
Solution
Devinez l'affichage : exceptions, lambdas, static
Classe générique Registre<K, V> avec map, lambdas et exceptions
Écrivez une classe générique Registre<K, V> construite sur une map<K, V> avec : definir(cle, valeur) (ajoute ou remplace), const V& obtenir(cle) const qui lance CleInexistante (dérivée de out_of_range, message avec la clé) si la clé est absente, contient(cle), taille(), une méthode générique clesOu(pred) qui retourne le vecteur des clés dont la valeur vérifie le prédicat, une méthode générique pourChaque(f) qui appelle f(cle, valeur) sur chaque entrée, et un operator<< ami qui affiche « cle = valeur » par ligne. Montrez l'utilisation avec un Registre<string, double> de prix : clés dont le prix dépasse 10, somme des prix via pourChaque, et traitement de CleInexistante.
Solution
Tout le code générique reste dans le .h, y compris les méthodes génériques clesOu et pourChaque, dont le type du prédicat est déduit à l'appel (une lambda différente à chaque fois). La liaison structurée const auto& [cle, valeur] remplace it->first / it->second.
Flotte de véhicules en Python
Traduisez en Python la hiérarchie de l'exercice de synthèse 2 de la page intra : Vehicule (immatriculation, masse, taxe() non implémentée, __str__), Voiture (places ; taxe = masse × 0,1), Camion (charge ; taxe = (masse + charge) × 0,2), et Flotte qui possède une liste de véhicules avec ajouter, taxe_totale(), nombre_de(classe), par_type() (dictionnaire nom de classe → liste), afficher_par_taxe() (du plus taxé au moins taxé) et trouver(immat) qui lance une exception VehiculeInconnu. Comptez les véhicules créés dans une variable de classe. Montrez un programme principal qui attrape l'exception.
Solution
Correspondances utilisées : classe abstraite → raise NotImplementedError ; static → attribut de classe ; dynamic_cast → isinstance ; map → dict avec setdefault ; sort + comparateur → sorted(..., key=lambda ...) ; accumulate → sum d'un générateur ; exception dérivée de Exception avec super().__init__(message).
5. Chronomètre d'entraînement
6. Liste de vérification
- Quiz des modules 8, 9 et 10 refaits jusqu'à 12 / 15, puis ceux des modules 1 à 7 une fois chacun.
- Les « Devinez l'affichage » des modules 8 à 10 et de cette page refaits sans regarder.
- Le laboratoire d'algorithmes du module 9 : pour chaque algorithme, réécrire la ligne de code de mémoire.
- Les cinq exercices de synthèse, chronomètre en marche ; puis les quatre de la page intra.
- Clé USB : site, aide-mémoire, TP1 à TP5, énoncés, exercices du simulateur de hockey.
- Lire l'énoncé en entier ; souligner les contrats (valeurs de retour, exceptions à lancer, « sans boucle », « au plus deux foncteurs »).
- Pour chaque conteneur : justifier le choix en un mot (indice, tri, unicité, clé, priorité).
- Pour chaque algorithme : intervalle
begin / end, lambda avec ses captures, résultat testé contreend(),eraseaprèsremove_if. - Pour chaque exception : classe dérivée,
throwavant modification,catchpar référence constante dans le bon ordre, reprise du programme. - Pour chaque classe : liste d'initialisation,
const,override, destructeur virtuel,staticdéfini hors classe,std::dans l'en-tête, code générique dans le .h. - En Python : indentation,
self,super().__init__,__str__,raise/except ... as e.