Résumé
Jusqu’ici, chaque collection du cours contenait des données d’un seul type : des notes, des statures, les cases d’un tableau à deux dimensions. Les entités d’un problème, en revanche, ont plusieurs attributs de types différents : un étudiant a un nom et une note ; un livre, un titre, un auteur et une année. Cette leçon présente l’enregistrement, le type composé que le programmeur définit lui-même avec struct pour regrouper ces attributs sous un seul nom. Elle commence par la raison d’être de l’enregistrement, la fragilité des vecteurs parallèles ; elle poursuit avec la définition d’un struct, ses champs avec valeur initiale et la création d’enregistrements avec des accolades et avec des initialiseurs désignés ; puis viennent les enregistrements comme paramètres et comme résultats de fonctions, et les vecteurs d’enregistrements, que l’on parcourt avec des liaisons structurées et que l’on trie et où l’on cherche selon un champ, avec la question du tri stable ; elle se termine par l’art de modéliser un problème avec des types qui reflètent son domaine.
Objectifs d’apprentissage
- Définir un
structavec des champs et des valeurs initiales, et créer des enregistrements par initialisation par accolades, y compris avec des initialiseurs désignés. - Écrire des fonctions qui reçoivent et renvoient des enregistrements, en choisissant le mode de passage.
- Trier un vecteur d’enregistrements et y chercher selon un champ avec
std::ranges::sort,std::ranges::stable_sortetstd::ranges::findmunis d’une projection, et distinguer un tri stable d’un tri qui ne l’est pas. - Modéliser avec des enregistrements les données d’un problème et justifier le choix des champs et des types.
Regrouper des données
Vecteurs parallèles
On suppose qu’un programme doit conserver, pour chaque étudiant d’un groupe, son nom, sa note et son âge. Avec ce que l’on a vu jusqu’à la leçon Tableaux à deux dimensions, la solution immédiate consiste en trois vecteurs, un par attribut, avec la convention que l’indice désigne la même personne dans les trois : names[i], grades[i] et ages[i]. On les appelle des vecteurs parallèles. Un tableau à deux dimensions ne convient pas, car ses cases doivent être toutes du même type, et un nom n’est pas un nombre.
#include <algorithm>#include <cstddef>#include <print>#include <string>#include <vector>int main(){ const std::vector<std::string> names{"Ana", "Luis", "Eva"}; std::vector<int> grades{6, 4, 7}; const std::vector<int> ages{19, 22, 20}; std::ranges::sort(grades); for (std::size_t i{0}; i < names.size(); ++i) { std::println("{} : note {}, {} ans", names[i], grades[i], ages[i]); }}Ana : note 4, 19 ansLuis : note 6, 22 ansEva : note 7, 20 ansLe programme voulait trier les étudiants par note, et il n’a trié que les notes. Il compile sans avertissement, et ni le détecteur d’erreurs de mémoire, AddressSanitizer, ni celui de comportement indéfini, UndefinedBehaviorSanitizer, n’ont rien à signaler ; pourtant, sa sortie est fausse : Ana avait un 6 et figure avec un 4. La convention qui liait les trois vecteurs n’est écrite nulle part où le compilateur puisse la lire, de sorte que rien n’empêche de la rompre. Il s’ensuit la fragilité des vecteurs parallèles : toute opération qui déplace, ajoute ou retire un élément doit être répétée, à l’identique et dans le même ordre, dans tous ; et il suffit d’en oublier un pour corrompre les données en silence.
Trois vecteurs parallèles face à un vecteur de fiches, chacune avec les trois données d’une personne.
Démonstration
La colonne 0 décrit une personne :
names[0],grades[0]etages[0]sont le nom, la note et l’âge d’Ana. Le lien n’est que l’indice.std::ranges::sort(grades)réordonne une seule ligne. Les deux autres ne s’en aperçoivent pas : la colonne 0 réunit maintenant le nom d’Ana, la note de Luis et l’âge d’Ana, qui ne sont à personne.L’alternative : un seul vecteur, dont les éléments sont des fiches. Chaque fiche contient les trois données d’une personne, et le lien entre elles n’est plus une convention sur les indices, mais la fiche elle-même.
Trier par note déplace des fiches entières : celle de Luis passe en première position avec le nom et l’âge qu’elle porte. Aucune donnée ne peut se séparer des autres, car l’opération ne les voit pas séparément.
Dès lors, ce que le problème demande est un type dont les valeurs soient des fiches : une valeur qui contienne un nom, une note et un âge, et que l’on copie, que l’on passe à une fonction, que l’on range dans un vecteur et que l’on trie comme un tout. C++ ne le fournit pas tout fait, car les fiches de chaque problème sont différentes ; mais il permet au programmeur de le définir. Ce type est l’enregistrement.
Chargement du contenu…