Préparation à l'intra
Stratégie par type de questionQuiz de révision (31 questions)Exercices de synthèseChronomètreListe de vérification
1. Préparer sa clé USB et ses feuilles
- Ce site fonctionne hors ligne : copiez le dossier complet du site sur la clé USB et ouvrez
index.htmlen double-cliquant. Les démos, quiz et solutions fonctionnent sans connexion. Vérifiez sur place que les fichiers s'ouvrent (l'énoncé du plan de cours le rappelle : aucun problème d'accès à vos fichiers ne sera pris en compte). - Imprimez l'aide-mémoire (Ctrl+P) : signatures des opérateurs, règle des trois, ordre de construction, casts, templates, en 4 à 5 pages.
- Mettez vos TP1, TP2 et TP3 corrigés sur la clé : ce sont des modèles complets de classes, constructeurs de copie, opérateurs, hiérarchie polymorphe et classe générique écrits dans le style attendu.
- Ajoutez les énoncés de TP (PDF) : ils contiennent les contrats exacts (préconditions, valeurs de retour) que les questions d'examen reprennent souvent.
2. Stratégie par type de question
« Donnez l'affichage du programme »
- Repérez chaque création d'objet (déclaration,
new, paramètre par valeur, retour par valeur, temporaire) : un constructeur par objet. Lequel ? Par défaut, par paramètres ou par copie. - Pour un objet dérivé : base (récursivement), puis attributs dans l'ordre de déclaration, puis corps. Les bases multiples dans l'ordre de la déclaration
class D : public B1, public B2. La liste d'initialisation ne change pas l'ordre. - Repérez chaque destruction : accolade fermante (ordre inverse de déclaration),
delete, fin d'expression pour un temporaire, fin de fonction pour un paramètre. Un objet dynamique sansdeleten'affiche jamais son destructeur. - Pour chaque appel de méthode via pointeur ou référence : virtuelle ? type réel. Non virtuelle ? type déclaré. Objet copié dans une variable de type base : découpage, version de la base.
a = bentre objets existants : opérateur d'affectation, pas constructeur de copie.T a = b: constructeur de copie.- Vérifiez les
endl: une ligne ou tout sur une ligne.
« Composition ou agrégation ? »
Attribut objet → composition. Attribut pointeur avec new dans la classe (ou unique_ptr) → composition par pointeur, la classe fait le delete. Pointeur ou référence reçu de l'extérieur sans new → agrégation, l'extérieur détruit. shared_ptr → partage. Un bon réflexe : écrire une phrase (« la classe possède / utilise ») et le nom de qui appelle delete.
« Donnez l'implémentation de la surcharge de l'opérateur ... »
Ouvrez le tableau des signatures (aide-mémoire). Déterminez : membre ou libre (opérande gauche) ; type de retour (valeur, T&, bool, ostream&) ; const ou non ; accès privé nécessaire (friend) ou interface publique suffisante. Écrivez la définition avec Classe:: si membre, sans si libre. Terminez par return *this; ou return out; quand il le faut.
« Quelles fonctions membres manquent ? » / « Faut-il modifier la classe ? »
Règle des trois : un pointeur brut possédé (avec delete dans le destructeur) exige constructeur de copie et opérateur d'affectation. Un attribut unique_ptr exige de les écrire pour que la classe soit copiable. Des attributs vector, string, shared_ptr, types simples : la copie par défaut suffit, justifiez-le (« pas de copie profonde nécessaire, l'opérateur de vector copie les éléments »).
« Combien de fois ... est appelé ? »
Constructeur de copie : T b(a), T b = a, paramètre par valeur, retour par valeur (sauf élision), new T(a), push_back(a). Jamais : passage par référence ou pointeur, affectation. Blocs mémoire : un par objet créé, tableau = n blocs, affectation = 0.
« Écrivez la classe / la hiérarchie »
Partez de l'énoncé : un nom par substantif, un attribut par donnée, une méthode par verbe. Puis : constructeur avec liste d'initialisation (base d'abord), accesseurs const, mutateurs avec validation (bool), destructeur virtuel si classe de base, méthodes virtuelles pures pour ce qui n'a pas de version commune, override dans les dérivées, std:: dans l'en-tête, définitions dans le même ordre.
3. Quiz de révision (modules 1 à 7)
31 questions, sans ordre de module
Comme à l'intra, les vrai/faux demandent une justification : lisez l'explication même quand vous avez juste.
La liste d'initialisation d'un constructeur permet d'initialiser les attributs composites et la classe de base.
- Faux
- Vrai
Un attribut privé de la classe de base est accessible aux classes dérivées.
- Faux
- Vrai
Soit la classe AA. Il est possible de surcharger l'opérateur de flux de sortie << comme une fonction membre de AA.
- Faux
- Vrai
Les surcharges « objet AA + int » et « int + objet AA » sont toutes deux des fonctions membres de AA.
- Faux
- Vrai
Déclaration d'un tableau de 10 pointeurs vers des objets AA, le tableau étant sur le tas :
AA* t = new AA[10];AA t[10];AA** t = new AA*[10];AA& t = new AA[10];
AA t[INTMAX]; (ii) tas d'objets : AA* t = new AA[INTMAX];Lorsqu'un paramètre porte le même nom qu'un attribut, peut-on accéder à l'attribut dans la méthode ?
- Non, l'attribut est masqué définitivement
- Oui, avec
this->attribut - Oui, avec
Classe.attribut - Oui, avec
::attribut
Pour surcharger <<, on retourne une référence. Vers quel objet, et pourquoi ?
- Vers l'objet affiché, pour le modifier
- Vers une copie du flux
- Vers this
- Vers l'objet ostream reçu, pour permettre les appels en cascade
cout << a << b réutilise le flux retourné.Si l'on doit choisir entre héritage et composition entre A et B, quelle est la stratégie ?
- Héritage si « A est un B », composition si « A a un B »
- Toujours l'héritage, plus puissant
- Toujours la composition, plus simple
- Héritage si B est abstraite
Quel est l'affichage ?
- ABab
- AABbaa
- AABbaa avec l'ordre : A A B b a a
- BAab
Combien d'appels au constructeur de copie ? void f(const T&); T g(T x) { return x; } T a; T b = a; f(b); T c = g(a);
- 1
- 3
- 2
- 4
T b = a (1) ; g(a) : paramètre par valeur (2), return x copie du paramètre dans la valeur de retour (3), qui devient c sans copie supplémentaire (élision). f par référence : 0.Laquelle de ces méthodes doit être const ?
void setCharge(double)Robot& operator+=(...)void recharger(double)double getConsommationW()
Qu'est-ce qui ne va pas ?
- Deux robots possèdent la même batterie : double delete à leur destruction
- Rien : la batterie est partagée
- Erreur de compilation
- Fuite de mémoire
Que vaut b après auto a = make_unique<X>(); auto b = std::move(a); ?
- nullptr
- Une copie de l'objet
- Le propriétaire de l'objet ; a est nul
- Erreur de compilation
Quel attribut traduit une relation d'agrégation ?
Moteur moteur_;Moteur* moteur_;reçu par le constructeur sans newunique_ptr<Moteur> moteur_;Moteur* moteur_ = new Moteur(...);
Signature correcte du constructeur de copie de Robot :
Robot(Robot autre);Robot(Robot* autre);Robot& Robot(const Robot& autre);Robot(const Robot& autre);
Dans operator=, que se passe-t-il sans le test this != &autre pour t = t; (classe avec int* possédé) ?
- On libère nos données puis on les copie depuis une mémoire libérée : comportement indéfini
- Rien, l'affectation est ignorée
- Erreur de compilation
- Les données sont dupliquées
Type de retour de l'opérateur ++ préfixe et de l'opérateur ++ suffixe, respectivement :
- valeur, valeur
- référence, référence
- référence, valeur
- void, int
*this) ; le suffixe retourne une copie de l'ancienne valeur, et prend un paramètre int fictif.Qu'affiche p->f() si f est virtuelle dans Base, redéfinie dans Derivee, et Base* p = new Derivee; ?
- La version de Base
- La version de Derivee
- Les deux
- Erreur de compilation
Même question si f n'est pas virtuelle.
- La version de Base
- La version de Derivee
- Comportement indéfini
- Erreur de compilation
Pourquoi le destructeur de Composante doit-il être virtuel ?
- Pour pouvoir dériver de Composante
- Pour que Composante soit abstraite
- Pour empêcher le découpage
- Pour que
delete(ou un pointeur intelligent) via unComposante*détruise aussi la partie dérivée
Une classe avec virtual double aire() const = 0; :
- Ne peut pas avoir de constructeur
- Ne peut pas avoir d'attributs
- Ne peut pas être instanciée, mais peut servir de type de pointeur ou de référence
- Doit être déclarée avec le mot-clé abstract
Que fait dynamic_cast<Chien*>(animalPtr) si l'objet pointé n'est pas un Chien ?
- Comportement indéfini
- Retourne nullptr
- Lance bad_cast
- Erreur de compilation
Chien c; Animal a = c; a.parler(); avec parler virtuelle redéfinie dans Chien. Quelle version ?
- Celle d'Animal : a est une copie découpée
- Celle de Chien : a vient d'un Chien
- Erreur de compilation
- Aucune : a est vide
Conversion ascendante (dérivée → base) :
- Exige dynamic_cast
- Exige static_cast
- Est interdite
- Est implicite et toujours valide
Composante* p = &moteur; compile sans cast.Où écrit-on la définition d'une méthode de Liste<T> ?
- Dans Liste.cpp
- Dans main.cpp
- Dans Liste.h (dans ou hors de la classe, avec
template <typename T>) - N'importe où
Avec template <typename T> T max(const T&, const T&), l'appel max(1, 2.0) :
- Retourne 2.0
- Ne compile pas (déduction contradictoire)
- Retourne 2
- Retourne 1
max<double>(1, 2.0).Dans class C : public A, public B, un membre x présent dans A et dans B :
- Doit être qualifié (
A::x) dans C, sinon l'accès est ambigu - Est celui de A par défaut
- Est celui de B par défaut
- Est fusionné en un seul
Que fait v.erase(v.begin() + 1) sur vector<int> v = {5, 6, 7} ?
- Retire 5
- Retire 7
- Retire 6 ; v devient {5, 7}
- Retire les deux premiers
Paramètre recommandé pour un vector<Robot> qu'on lit seulement :
vector<Robot> vconst vector<Robot>& vvector<Robot>* vconst vector<Robot> v
Que vaut p.use_count() après auto p = make_shared<X>(); { auto q = p; auto r = p; } ?
- 3
- 0
- 2
- 1
Une méthode const peut :
- Modifier ses variables locales et ses paramètres, pas les attributs de l'objet
- Modifier les attributs si elle les remet ensuite
- Appeler n'importe quelle méthode de l'objet
- Retourner une référence non constante vers un attribut
4. Exercices de synthèse (format examen)
Chaque exercice mélange plusieurs modules, comme une question longue d'intra. Écrivez le code en entier avant de regarder la solution.
Classe Inventaire avec tableau dynamique
Un Inventaire conserve des quantités (int) dans un tableau alloué dynamiquement de capacité fixée au constructeur, et un nombre d'éléments utilisés. Écrivez la classe avec : constructeur Inventaire(int capacite), destructeur, constructeur de copie, opérateur d'affectation, bool ajouter(int quantite) (faux si plein), int operator[](int i) const (0 si indice invalide), Inventaire& operator+=(int quantite) (comme ajouter, avec chaînage), int total() const, et operator<< ami qui affiche les quantités séparées par des espaces. Expliquez en une phrase pourquoi la règle des trois s'applique.
Solution
Règle des trois : la classe possède un bloc alloué avec new[] (composition par pointeur brut) ; la copie par défaut copierait l'adresse, d'où double libération et partage involontaire. Ordre des attributs dans la liste : capacité, taille, puis le tableau (qui utilise capacite, le paramètre, pour ne pas dépendre de l'ordre).
Hiérarchie de véhicules et flotte polymorphe
Un Vehicule (abstrait) a une immatriculation et une masse ; il sait calculer sa taxe() (virtuelle pure) et s'afficher (virtuelle, affiche immatriculation et masse). Une Voiture ajoute un nombre de places : taxe = masse × 0,1. Un Camion ajoute une charge maximale : taxe = (masse + charge) × 0,2. Une Flotte possède ses véhicules (ils sont créés pour elle et détruits avec elle) dans un seul conteneur, calcule la taxe totale et affiche chaque véhicule selon son type. Écrivez les classes, puis une fonction int nombreDeCamions(const Flotte&) (la Flotte expose const vector<unique_ptr<Vehicule>>& getVehicules() const). Quel type de pointeur choisir dans Flotte et pourquoi ? Faut-il un destructeur virtuel ?
Solution
unique_ptr : les véhicules ne sont jamais partagés et meurent avec la flotte ; vector<Vehicule> est impossible (classe abstraite, et découpage). Destructeur virtuel : oui, les objets sont détruits à travers des unique_ptr<Vehicule>.
Équipe de joueurs partagés
Un Joueur a un nom et un salaire. Une Equipe a un nom et des joueurs partagés entre équipes (un joueur prêté apparaît dans deux équipes ; modifier son salaire est visible partout). Écrivez Equipe avec : Equipe& operator+=(shared_ptr<Joueur>) (refuse nullptr et les doublons), Equipe& operator-=(shared_ptr<Joueur>), double masseSalariale() const, shared_ptr<Joueur> operator[](size_t i) const, bool operator==(const Equipe&) const (mêmes joueurs, même ordre) et operator<<. Faut-il écrire un constructeur de copie ? Que donne la copie d'une équipe ?
Solution
Pas de constructeur de copie à écrire : string et vector de shared_ptr se copient correctement par défaut. La copie d'une équipe est une nouvelle équipe qui partage les mêmes joueurs (use_count augmente) : retirer un joueur de la copie ne le retire pas de l'original, mais changer son salaire est visible des deux côtés.
Conteneur générique de pointeurs polymorphes
Écrivez une classe générique Registre<T> qui conserve des shared_ptr<T> (sans doublons d'objet) avec ajouter, size, operator[] const et une méthode générique template <typename D> vector<shared_ptr<D>> filtrer() const qui retourne les éléments dont le type réel est D ou une dérivée de D. Utilisez-la avec Composante / Moteur / Capteur pour obtenir les moteurs d'un robot. Quelle condition sur Composante rend filtrer possible ?
Solution
Condition : Composante doit être polymorphe (au moins une méthode virtuelle, en pratique virtual ~Composante()), sinon dynamic_pointer_cast ne compile pas. Tout le code reste dans le .h.
5. Chronomètre d'entraînement
6. Liste de vérification
- Site copié sur la clé USB, aide-mémoire imprimé, TP sur la clé.
- Quiz de chaque module refait : score au-dessus de 12 / 15.
- Les quatre « Devinez l'affichage » (M1, M3, M4, M5) refaits sans regarder.
- Une heure sur les exercices de synthèse, chronomètre en marche.
- Lire tout l'énoncé ; repérer les contrats (préconditions, valeurs de retour) avant d'écrire.
- Pour chaque classe : liste d'initialisation, const sur les accesseurs,
std::dans les en-têtes,override, destructeur virtuel si base. - Pour chaque opérateur : membre ou libre, type de retour, const,
return *this/return out. - Pour chaque « affichage » : compter constructions et destructions, vérifier virtuel ou non,
endl. - Justifier les vrai/faux en une phrase avec le mot-clé attendu (liaison dynamique, découpage, règle des trois...).