Ce n’est pas pratique de ne pas avoir de diff, pour suivre les corrections sans devoir tout relire. Pour ça que j’aime bien rédiger sur GitHub puis copier-coller sur ZdS.
Remarque générale sur le cours et la pédagogie (en prenant le chapitre "Une classe de grande valeur" comme exemple). Une question qui revient souvent chez les débutants, c’est : "parmis toutes les syntaxes que j’ai appris, laquelle choisir ?" Cela correspond à la question du pourquoi des syntaxes.
En termes pédagogique, cela veut dire de penser les chapitres par rapport aux problématiques que l’on veut présenter, pas les syntaxes.
Ce chapitre présente le même problème : "pourquoi la sémantique de valeur ?"
Copier des objets
Peut être commencer par faire un rappel sur les copies qu’ils ont déjà vu et (normalement) manipulés. Par exemple :
-
Montrer que les exemples suivants sont comparable avec == donc ce sont des sémantiques de valeurs
-
copie de type fondamentaux (qui sont des semantiques de valeurs, mais ne sont pas des classes)
- structure user-def, on peut copier parce que le compilateur ajoute automatiquement ce qu’il faut.
struct A { int i; };
A a;
A b { a };
- exemple de classe connues et copiables. Autres exemple de classe de valeur : std::vector, iterqteurs, std::pair, etc.
il faut alors définir un constructeur de copie.
Cf juste avant, ils ont normalement déjà vu des cas où il est possible de copier, mais sans avoir défini de contructeur par copie. Il faut en parler rapidement (au moins pour dire que ca sera détaillé plus tard).
Reprenons notre classe Fraction (j’ai simplifié une partie du code pour plus de facilité et de lisibilité) et implémentons le constructeur de copie.
En terme de progression pédagogique, les lecteurs voient :
- la copie de structure de données sans avoir besoin de déclarer quoi que ce soit pour faire une copie (dans les chapitres précédants, sans rappel dans ce chapitre)
- la syntaxe pour déclarer et définir la copie
- la sytnaxe pour déclarer la copie et laisser le compilateur s’occuper d’écrire la copie
Donc pas forcément une progression logique en termes de complexité du code et des notions.
A mon sens, il serait plus progressif de faire :
- rappel sur la copie implicite (faites automatiquement par le compilateur)
- présenter la déclaration de la copie (définition implicite).
- puis la syntaxe la plus complexe : la déclaration et définition implicite
int m_numerateur;
int m_denominateur;
Je ne suis pas un grand fan des attributs non initialisés.
Constructeur de copie
Opérateur d’affectation par copie
"par" ou "de" ?
Normalement, ils ont vu les paramètres par défaut dans le chapitre sur les fonctions (?). Ne faudrait-il pas mettre une note pour dire que c’est utilisable aussi avec les constructeurs ? (Exemple avec Fraction(int n, int d = 1);
)
Et du coup, parler aussi du mot clé explicit
. (Et du coup, le titre "Soyons explicites" peut etre confondu avec ce mot clé)
Le compilateur, qu’il est choux !
Du coup, vous présentez une syntaxe, puis juste après, vous dites que ça sert à rien…
Le lecteur peut légitiment se poser la question "pourquoi ?" (pourquoi avoir besoin de connaitre cette syntaxe ? Quand avoir besoin de l’écrire explicitement ?)
Si on suit les bonnes pratiques (Big-0), on ne devrait pas avoir à écrire explicitement la copie. (Est-ce que vous parlez de Big-0 quelque part ?). Donc en gros, on explique une syntaxe qu’ils ne devront pas utiliser s’ils suivent les bonnes pratiques…
Cela rejoint ce que je disais au début : présenter selon les problématiques qu’on veut résoudre. Ici, on présente la copie (écrire la copie, pas l’utiliser) parce qu’elle répond a une problématique ou juste parce qu’on a toujours organisé les cours POO de C++ comme ça ?
(J’utilise "on" parce que je présentais la copie aussi dans le chapitre 2 de la POO. Mais c’est peut être pas une bonne idée)
On dit d’une classe qu’elle a une sémantique de valeur si deux objets au contenu identique peuvent être considérés comme égaux.
(Retour au début du chapitre). On définie la sémantique de valeur par la notion d’égalité, mais le chapitre ne parle pas de l’égalité, il parle de la copie (qui est une notion discutable à présenter). Il faudrait présenter en premier la notion d’égalité (et Shika me fait remarquer en live que si on a des notions de théorie des langages, cette notion d’égalité n’est pas trivial, il existe plusieurs concepts d’égalité).
La déf en C++ : https://en.cppreference.com/w/cpp/named_req/EqualityComparable donc :
- a et b meme type T
- a == b = true -> égalité
Je me demande s’il faut tout simplement déplacer la copie en annexe et parler directement de la surcharge d’opérateurs dans la sémantiques de valeur (en commançant par ==).
(Note : je viens de revoir le chapitre 6, la notion de surcharge d’opérateur à déjà été vu. Donc aucun problème à aborder cela dans ce chapitre)
Les opérateurs d’affectation intégrés
C’est pas l’écriture idiomatique (https://en.cppreference.com/w/cpp/language/operators) qui est utilisée. Pas sur que ce soit une bonne idée par rapport aux bonnes pratiques.
Fraction& Fraction::operator+=(Fraction const & autre_fraction)
Fraction operator+(Fraction a, Fraction const & b)
Peut être une note par rapport à pourquoi et quand avoir des fonctions libres et des fonctions membres ?
Avez-vous remarqué que le code pour écrire += est quasiment identique à celui utilisé pour + ?
Le lecteur n’aura probablement pas remarqué cela, s’il a lu le chapitre 6 il y a plusieurs semaines.
Soyons intelligents, réutilisons
Fraction f3 { f1 + f2 };
Peut être const.
En toute amitié
La notion d’amitié n’est pas liée aux opérateurs. Il ne faudrait pas présenter cette notion en même temps que private
et public
? (En faisant attention a l’encapsulation)
d’une classe qu’elle est amie
L’amité classe-classe est citée mais pas expliquée ou présentée en pratique.
Or, si l’on veut l’implémenter comme membre de notre classe Fraction, on a alors le premier paramètre qui doit être de même type que la classe, c’est-à-dire Fraction.
La distinction entre fonction libre et membre n’est peut pas tres bien définie, ainsi que les sybtaxes correspondantes.
Vous pouvez vous servir du code suivant comme base, pour vous donnez une idée de comment la classe est utilisée.
Ca serait peut etre mieux de :
- soit de demander dans un premier temps de définir eux même l’API et les tests unitaires
- soit donner les tests unitaires, puis leur demander de définir l’API puis l’implémentation
n’êtes pas très à l’aide en mathématiques
"très à l’aise"
(Et ça manque de const)
Certaines classes ont une sémantique de valeur.
Pas fan de la formulation. Une classe n’a pas une sémantique, on choisit de donner une sémantique à une classe. Pour certaines raisons. Mais comme la question du pourquoi n’est pas abordée, le lecteur ne connait pas ces raisons (cf ma première remarque).
opérateur d’affection par recopie
"par recopie" ou "par copie" ? (Ou "de copie")
Petite note sur le chapitre suivant (je ferais la relecture complète au prochain live).
Pour être sûrs d’avoir bien codé, il est important de tester son code, vous le savez déjà. Alors, pourquoi en serait-il différent pour ce T.P ?
Du coup, si c’est important, pourquoi ce n’est pas dans la correction ?
Ce qui me gène dans la présentation des solutions d’exos et TP, ce qu’on laisse penser aux lecteurs que résoudre une problème de programmation, c’est pondre un code. Or, ça devrait être une démarche avant tout, de résolution de problème : comment aborder un problème, quels sont les étapes de sa résolution, etc. Cela devrait appraitre dans les solutions données (principe de donner le bon exemple, pour que l’apprenant suive cet exemple).
EDIT : j’ai fait la review en live, j’ai donné plus de détails a l’oral.